web-dev-qa-db-fra.com

Longueur maximale de la méthode Intent putExtra? (Forcer la fermeture)

J'ai besoin d'aide pour déboguer mon application. Tout d'abord: dans l'émulateur et sur certains autres appareils, mon application fonctionne correctement. Sur mon appareil, j'ai obtenu une fermeture forcée (sans message de fermeture forcée).

Le "plantage" se produit si l'activité de l'application est modifiée.

Voici du code de la classe MainActivity. Il lit simplement le contenu html d'une page Web sur une vue Web. Et non, il n'est PAS possible de le faire sur HttpRequest car je n'ai pas pu simuler la demande de publication.

public class MainActivity extends Activity {

    public final static String EXTRA_HTML = "com.example.com.test.HTML";

    private WebView mWebView;
    private ProgressDialog mDialog;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);  
        mWebView = (WebView) findViewById(R.id.webView1);
        CookieSyncManager.createInstance(this);
        CookieManager cookieManager = CookieManager.getInstance();
        cookieManager.removeAllCookie();
        mWebView.setBackgroundColor(0);
        mWebView.setWebChromeClient(new WebChromeClient() {
            public boolean onConsoleMessage(ConsoleMessage cmsg) {
                if (cmsg.message().startsWith("MAGIC")) {
                    mDialog.cancel();
                    /*HashMap<String, String> message = new HashMap<String, String>();*/
                    String msg = cmsg.message().substring(5);
                    Intent intent = new Intent(MainActivity.this,
                        ReadDataActivity.class);
                    /*message.put("message", msg);*/
                    /*intent.putExtra(EXTRA_HTML, message);*/
                                    intent.putExtra(EXTRA_HTML, msg);
                    startActivity(intent);
                }
                return false;
            }
        });
        mWebView.getSettings().setJavaScriptEnabled(true);
        mWebView.getSettings().setPluginState(PluginState.OFF);
        mWebView.getSettings().setLoadsImagesAutomatically(false);
        mWebView.getSettings().setBlockNetworkImage(true);
        mWebView.getSettings().setAppCacheEnabled(true);
        mWebView.getSettings().setSavePassword(true);
        mWebView.getSettings()
                .setCacheMode(WebSettings.LOAD_NORMAL);
        mWebView.setWebViewClient(new WebViewClient() {

            public void onPageFinished(WebView view, String address) {
                if (address.indexOf("mySession") != -1) {
                    view.loadUrl("javascript:console.log('MAGIC'+document.getElementsByTagName('html')[0].innerHTML);");
                }
});

                mWebView.loadUrl("http://www.myurl.de");

}

Donc, dans la méthode onConsoleMessage() je passe juste le code html à une autre classe Activity qui lit, analyse et affiche le contenu.

Le problème est maintenant qu'à ce stade, lorsque la classe ReadDataActivity doit être chargée, il suffit de fermer l'application et de revenir à l'écran d'accueil sans aucun message ni boîte de dialogue utilisateur.

Est-il possible que le code html qui est passé en tant que chaîne au ReadDataActivity soit trop gros? J'essaie également d'ajouter le code html en tant que chaîne dans un HashMap mais le problème est le même.

Quelques idées que je peux faire pour déboguer le problème? Peut-être que je devrais essayer de créer un objet Parcelable?

Dans l'émulateur, tout fonctionne bien.

27
sk2212

D'après mon expérience (il y a quelque temps), vous pouvez mettre jusqu'à 1 Mo de données dans un Bundle encapsulé dans Intent. Je pense que cette restriction était valable jusqu'à Froyo ou Gingerbread.

Cependant, afin de surmonter ce problème, je vous suggère d'enregistrer votre contenu sur un fichier temporaire et de transmettre le chemin/URI de votre fichier temporaire à votre deuxième activité. Ensuite, dans votre deuxième activité, lisez le contenu du fichier, effectuez l'opération souhaitée et supprimez enfin ce fichier.

Si vous le souhaitez, vous pouvez également intégrer Shared_Preferences pour cette tâche - si vous pensez que la gestion des fichiers est lourde.

50
waqaslam

J'ai fait quelques recherches sur la quantité maximale de données que vous pouvez transférer en utilisant une intention. Et il semble que la limite soit loin de 1 Mo ou 90 Ko, elle ressemble plus à 500 Ko (testé sur les API 10, 16, 19 et 23).

J'ai écrit un article de blog sur ce sujet, vous pouvez le trouver ici: https://www.neotechsoftware.com/blog/Android-intent-size-limit

14
Rolf ツ

La limite de taille de l'intention est toujours assez faible dans Jelly bean , ce qui est légèrement inférieur à 1 Mo (environ 90 Ko), vous devez donc toujours être prudent sur la longueur de vos données, même si votre application ne cible que les derniers = Android.

4
NullNoname

La taille fixe de 1 Mo n'est pas seulement limitée aux intentions. En tant qu'intentions, fournisseurs de contenu, Messenger, tous les services système comme le téléphone, le vibreur, etc. utilisent le fournisseur d'infrastructure IPC de Binder. De plus, les rappels du cycle de vie de l'activité utilisent également cette infrastructure.

1 Mo est la limite globale de toutes les transactions de liant exécutées dans le système à un moment particulier.

Dans le cas où de nombreuses transactions se produisent lorsque l'intention est envoyée, elles peuvent échouer même si les données supplémentaires ne sont pas volumineuses.
http://codetheory.in/an-overview-of-Android-binder-framework/

4
Shinoo Goyal

Un peu tard pour le match, mais je me suis juste heurté au même problème. L'écriture des données dans un fichier n'avait pas vraiment de sens en termes de performances dans mon cas, mais je suis tombé sur cela dans ma recherche de réponses:

http://developer.Android.com/guide/faq/framework.html#

L'utilisation d'un singleton est meilleure pour moi car il n'y a pas besoin d'E/S disque. Meilleures performances si les données n'ont pas besoin d'être conservées.

Voici un exemple d'implémentation:

public class DataResult {

    private static DataResult instance;
    private List<SomeObject> data = null;

    protected DataResult() {

    }

    public static DataResult getInstance() { 
        if (instance == null) {
            instance = new DataResult();
        }
        return instance;
    }

    public List<SomeObject> getData() { return data; }
    public void setData(List<SomeObject> data) { this.data = data; }
}

Ensuite, vous pouvez définir l'utilisation de ceci dans une seule activité:

DataResult.getInstance().setData(data);

Et obtenez-le dans l'autre activité comme celle-ci:

List<SomeObject> data = DataResult.getInstance().getData();
2
monkeybuffer

J'ai vu qu'en écrivant et en lisant un fichier, il y avait moins de performances. Alors j'ai vu cette solution: . J'utilise donc cette solution:

public class ExtendedDataHolder {

    private static ExtendedDataHolder ourInstance = new ExtendedDataHolder();

    private final Map<String, Object> extras = new HashMap<>();

    private ExtendedDataHolder() {
    }

    public static ExtendedDataHolder getInstance() {
        return ourInstance;
    }

    public void putExtra(String name, Object object) {
        extras.put(name, object);
    }

    public Object getExtra(String name) {
        return extras.get(name);
    }

    public boolean hasExtra(String name) {
        return extras.containsKey(name);
    }

    public void clear() {
        extras.clear();
    }

}

Ensuite, dans MainActivity, je l'ai appelé comme suit:

ExtendedDataHolder extras = ExtendedDataHolder.getInstance();
extras.putExtra("extra", new byte[1024 * 1024]);
extras.putExtra("other", "hello world");

startActivity(new Intent(MainActivity.this, DetailActivity.class));

et en détailActivité

ExtendedDataHolder extras = ExtendedDataHolder.getInstance();
if (extras.hasExtra("other")) {
    String other = (String) extras.getExtra("other");
}
2
Sultan Ahmed Sagor

L'utilisation de static String la variable est bonne. Si l'utilisateur a besoin de faire des allers-retours entre différents éléments HTML, vous pouvez également utiliser LruCache comme ceci:

    static LruCache<String, String> mMemoryCache;
    final int kiloByte = 1024;
    .
    .
    final int maxMemoryKB = (int) (Runtime.getRuntime().maxMemory() / kiloByte);
    // then choose how much you want to allocate for cache
    final int cacheSizeKB = maxMemoryKB / 8;
    .
    .
    mMemoryCache = new LruCache<String, String>(cacheSizeKB) {
        //@Override
        protected int sizeOf(String key, String value) {   
            try {
                byte[] bytesUtf8 = value.getBytes("UTF-8");
                return bytesUtf8.length / kiloByte;
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            return -1;
        }
    };
    .
    .
    String cacheKey = generateUniqueString(key);
    if (mMemoryCache.get(key) == null) {
        mMemoryCache.put(cacheKey, yourContent);
    }

    Intent intent = new Intent(getApplicationContext(), ReadDataActivity.class);
    intent.putExtra(EXTRA_HTML, cacheKey);
    startActivity(intent);

Puis du côté ReadDataActivity

Intent intent = getIntent();
String cacheKey = intent.getStringExtra(EXTRA_HTML);
String contentString = MainActivity.mMemoryCache.get(cacheKey);
doSomethingWith(contentString);

Cette idée est venue de ici .

1
rockhammer

Le tampon de transaction Binder a une taille fixe limitée - 1 Mo. Mais le problème est que tampon partagé par toutes les transactions en cours pour le processus.

Essayez donc de garder les données de votre intention aussi petites que possible à chaque fois.

1
Anatoliy Shuba

Une solution alternative pour transmettre de grandes données entre les activités consiste à utiliser un champ statique. Dans votre cas, ajoutez cette ligne à la classe ReadDataActivity

public static String msg;

Ensuite, vous pouvez utiliser le msg de champ statique dans la classe MainActivity comme suit

ReadDataActivity.msg = cmsg.message().substring(5); 

Et enfin commencez votre activité sans mise supplémentaire

Intent intent = new Intent(MainActivity.this, ReadDataActivity.class);                  
startActivity(intent);
1
Zakaria