Best practice to handle orientation change: Androi

2019-01-20 23:29发布

问题:

I was going through various practices to handle orientation change with threads and AsyncTask. I came across following solutions:

  1. Attach-detach model : Attaching and detaching activity to threads and AsyncTask while preserving their instance. (Source: 1, 2)

  2. Headless fragment way : Using a non-UI/headless fragment to do all the thread related operations and retaining its instance on configuration change. (Source: 1, 2)

Are there any other approaches to handle this scenario? What is the recommended practice? I'm asking this because I couldn't find a generic solution anywhere in the Android docs.

回答1:

Some summaries

There are several methods mentioned above that are good practices but I thought I might sum them up with short explanations. Below are some of the most popular libraries being used currently for http networking, asynchronous work / threading, and caching.

My current project (just preferences)

I personally am currently using Otto, Loaders, Volley, Ormlite, and a network stack based on Apache and Services. I do hope to replace, the network stack at some point with either Volley, Retrofit, and maybe eventually Robospice.

I personally very much like Otto and Volley


RoboSpice (Modular)

  • https://github.com/octo-online/robospice
  • http://www.youtube.com/watch?v=ONaD1mB8r-A
  • a plugin / modular approach to long-running tasks
  • this is like the "swiss-army-knife" of libraries, but you need to know what each tool does.
  • Handles REST calls
  • persists data through orientation and other changes
  • can handle disk and memory caching )
  • works with various HTTP libraries and persistence libraries (Gson, Jackson, Spring, OkHttp, and many of the below libraries)
  • beta for Ormlite support, I think

Retrofit (REST)

  • https://github.com/square/retrofit
  • Annotation library to make REST very easy. Works with Robospice.

Volley (Networking data & Images)

  • https://android.googlesource.com/platform/frameworks/volley
  • https://developers.google.com/events/io/sessions/325304728
  • This is the networking code that runs the Google Play Store
  • Fast, reliable
  • Handles most caching for you with some sensible defaults
  • very easy to use
  • built specifically for very fast image, json, etc loading
  • Handles all threading for you.

Picasso (images)

  • https://github.com/square/picasso
  • Http library for loading images
  • fast
  • very easy to use

Loaders (Android)

  • well supported
  • persist through orientation change and save/load of fragment state
  • can be difficult to get right
  • no caching

AsyncTask (Android)

  • simple way for background work from the UI thread
  • must be canceled and be careful about tasks that return after an activity or fragment is torn down.

Otto (event bus)

  • https://github.com/square/otto
  • Event bus that makes a-sync work between components and fragments easy
  • Very powerful @Produce ability retains the last event and can produce it on demand for any new interested subscribers to the bus

Headless Fragments (?)

  • I personally have never seen this used other than Vogella's tutorials, so I'm not sure on this one.

Service (Android)

  • The old school way
  • ultimate control, you must do everything yourself
  • usually used with Appache or HURL client and
  • pass Parcels around via Intents


回答2:

Why don't you try Loaders, in particular AsyncTaskLoader? They are available for pre-Honeycomb through Support Library and perfectly match Activity/Fragment lifecycle. Here is the official summary:

  • They are available to every Activity and Fragment.
  • They provide asynchronous loading of data.
  • They monitor the source of their data and deliver new results when the content changes.
  • They automatically reconnect to the last loader's cursor when being recreated after a configuration change. Thus, they don't need to re-query their data.


回答3:

We are actually using RoboSpice library. It runs on a Service with only providing RequestListeners objects.

The problem with your first approach (Keeping references between the AsyncTask) is that you can probably generate memory leaks because when your AsyncTasks holds your Activities references, they will be not garbage collected. Keep an eye on this just profiling your application checking Heap Size rotating the same Activity over and over again. Your heap should grow in the normal parameters (There is a moment when your objects that must be garbage collected lives at the same time with new objects) but when GC runs your RAM allocation should fall to the same size that you've allocated at the beginning.

So if I have to recommend something will be the next thing:

Activity managing API Calls and Flows (With RoboSpice, letting de UI rotate) Simple screens inside Fragments using retainInstance in true. This let to you pass your DTOs directly to your fragments, and you have to only manage the state at the top level Activity.



回答4:

If handling asyncTask is your main concern i.e not willing to download data each time orientation is changed then you may try like this --

(1) Initialize any value before on create like this ..

Boolean android_hacker = false;

(2) Now when you are done with downloading data on AsyncTask class then set that value to true

android_hacker = true;

Here maintain all data utilizing model and Array adapter class

(3) Now each time orientation is changed then check like this

if( android_hacker = true ){

// Use your saved instance ..

}else{

// Download data as it is yet not downloaded ..

}

Hope it helps ..



回答5:

There are many ways you can try beside the AsyncTask. And if you try to find a best practice, AsyncTask isn't a good option. This answer explains why you should not use AsyncTask. And they recommend you using a better way which can deal with long running task, RoboSpice.
I have already used this library and I think it is worthy to try: respect activities lifecycles (orientation change), no memory leaks, supports multi-threading, caches results... It can plug and unplug long request task by using cache (but it can't work well for a non-cache request).

But I recommend a good way comes from Google: IntentService and BroadcastReceiver. You will registered and unregistered broadcast during orientation change to receive the data result. All background task will work in IntentService and notify whatever you want to activity by BroadcastReceiver. There are a lots of example that you can try. Something like this: http://mobile.tutsplus.com/tutorials/android/android-fundamentals-intentservice-basics/

Update:

Hi R4j, the point is my application is quiet complex. And I've to make number of parallel network calls. Your approach with IntentService is good but isn't suitable for complex scenarios

I don't think this is a problem. You can do anything with the IntentService, even the complicated tasks. If you want parallel tasks, you may consider a Service with multithreading in it and communicate with activity by Intent. Sending intent between Service and activity is safe and flexible, that is Android way.
And if you want to cache (by file download, stream, by database..) RoboSpice is a best choice for you



回答6:

You can try with the following approaches:

1) If your application does not explicitly require any orientation changes, just disable orientation changes at the beginning of app execution, thereby you would be avoiding any crashes or related problems with respect to orientation changes.

This you can do using the following line in the outermost layout of your layout xml file:

  android:orientation="vertical"

(for setting vertical orientation)

2) You can set or preserve previous orientation values at the beginning of your thread execution using Asynctask, as follows (syntax example only):

  setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

and

  getResources().getConfiguration().orientation