Android获取数据过程中旋转屏幕问题
2016-01-27 21:15
633 查看
Android开发默认情况下旋转屏幕会重新创建Activity对象,这个过程中会先调用旧的Activity对象的onSaveInstanceState和onDestroy方法,然后调用新Activity的onCreate和onRestoreInstanceState方法。如果启动AsyncTask后台获取数据时旋转屏幕,由于没有绑定新的Activity对象,获取到的数据不会显示出来,另外,由于旧的Activity对象依然被引用,不会被垃圾回收,存在内存泄露。
先看一个例子:
创建一个Activity,用于显示一个字符串列表
创建一个Presenter,用于处理数据
PS:MVP结构,Activity做为view专门负责显示UI逻辑,Model则负责处理业务和数据,Presenter作为view和model的桥梁
这里只用到view和presenter,模型部分忽略。
Activity代码:
布局:
Presenter代码:
点击按钮开始获取数据,可以看到不久后数据更新了。
但是如果在点击按钮后旋转屏幕,则看不到数据更新,因为presenter绑定的是旧的activity对象,数据并没有更新到新的activity上。
解决办法:
1.设置旋转屏幕时不重新创建Activity
在AndroidManifest.xml中activity组件定义中设置android:configChanges属性
旋转屏幕后不会调用Activity的onDestroy方法。
可以看到数据更新成功,这是比较简单的方法。
2.将Presenter对象绑定到新的Activity对象
使用Application对象作为桥梁,在创建新activity前将presenter对象缓存,在新activity的onCreate方法中将presenter对象重新绑定。
Application代码:
记得在AndroidManifest.xml中设置这个application对象:
Presenter中添加重新绑定view接口:
Activity中添加如下代码:
在onCreate方法添加:
这样就可以看到数据更新成功了。
由于旧的activity对象没有被引用,可以愉快地被回收。
不过这里还没结束,还要考虑让presenter对象愉快地被回收。
不管屏幕旋转多少次,缓存里面只有一个presenter对象,当最后一次activity退出时,缓存被清空,presenter对象也就能被回收。
先看一个例子:
创建一个Activity,用于显示一个字符串列表
创建一个Presenter,用于处理数据
PS:MVP结构,Activity做为view专门负责显示UI逻辑,Model则负责处理业务和数据,Presenter作为view和model的桥梁
这里只用到view和presenter,模型部分忽略。
Activity代码:
public class SampleActivity extends BaseActivity implements UserListView,OnItemClickListener { private static final String TAG = SampleActivity.class.getName(); private UserListPresenter mPresenter; private ArrayAdapter<String> mAdapter; private String[] data = new String[3]; @Bind(R.id.main_list_view) ListView mListView; @Bind(R.id.btn_get_data_by_asynctask) Button mBtnAsyncTask; @Bind(R.id.view_loading) TextView mTextViewLoading; @Bind(R.id.view_loading_progress) TextView mTextViewLoadingProgress; @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); setContentView(R.layout.activity_sample); Log.d(TAG,"onCreate,savedInstanceState:" + savedInstanceState); mPresenter = new UserListPresenter(this); ButterKnife.bind(this); data[0] = "first line"; data[1] = "second line"; data[2] = "third line"; mAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, data); mListView.setAdapter(mAdapter); mListView.setOnItemClickListener(this); } @OnClick(R.id.btn_get_data_by_asynctask) void getDataByAsyncTask(){ Log.d(TAG,"getData"); mPresenter.getUserListByAsyncTask(); } @Override public void onItemClick(AdapterView<?> arg0, View v, int position, long id) { // TODO Auto-generated method stub Log.d(TAG,"onItemClick,position:" + position); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onRestoreInstanceState(savedInstanceState); Log.d(TAG,"onRestoreInstanceState"); } @Override protected void onSaveInstanceState(Bundle outState) { // TODO Auto-generated method stub super.onSaveInstanceState(outState); Log.d(TAG,"onSaveInstanceState"); } @Override protected void onDestroy() { // TODO Auto-generated method stub super.onDestroy(); Log.d(TAG,"onDestroy"); mPresenter.destroy(); mPresenter = null; } @Override public void showLoading() { // TODO Auto-generated method stub mTextViewLoading.setVisibility(View.VISIBLE); } @Override public void hideLoading() { // TODO Auto-generated method stub mTextViewLoading.setVisibility(View.GONE); } @Override public void showLoadingProgress(Integer... values) { // TODO Auto-generated method stub mTextViewLoadingProgress.setVisibility(View.VISIBLE); mTextViewLoadingProgress.setText("loading progress:" + values[0]); } @Override public void hideLoadingProgress() { // TODO Auto-generated method stub mTextViewLoadingProgress.setVisibility(View.GONE); } @Override public void showRetry() { // TODO Auto-generated method stub } @Override public void hideRetry() { // TODO Auto-generated method stub } @Override public void showError(String message) { // TODO Auto-generated method stub } @Override public Context getContext() { // TODO Auto-generated method stub return this; } @Override public void viewUserList(Collection<UserEntity> userList) { // TODO Auto-generated method stub if (userList == null) { return; } Log.d(TAG,"viewUserList,mAdapter:" + mAdapter + ",mListView:" + mListView); List<UserEntity> users = (List<UserEntity>)userList; int len = userList.size(); StringBuffer sb = new StringBuffer(); for (int i = 1; i < len+1; i++) { sb.setLength(0); UserEntity user = users.get(i-1); sb.append("name:"); sb.append(user.getFullName()); sb.append("\n"); sb.append("desc:"); sb.append(user.getDescription()); data[i] = sb.toString(); } mAdapter.notifyDataSetChanged(); } }
布局:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.example.mytestapp.view.SampleActivity" > <Button android:id="@+id/btn_get_data_by_asynctask" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="get data by AsyncTask" /> <TextView android:id="@+id/view_loading" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/btn_get_data_by_asynctask" android:text="loading..." android:visibility="gone" /> <TextView android:id="@+id/view_loading_progress" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/btn_get_data_by_asynctask" android:layout_toRightOf="@id/view_loading" android:text="loading progress:" android:visibility="gone" /> <TextView android:id="@+id/text_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/view_loading_progress" android:text="user list:" /> <ListView android:id="@+id/main_list_view" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/text_view" android:layout_centerHorizontal="true" /> </RelativeLayout>
Presenter代码:
public class UserListPresenter extends BasePresenter implements Presenter { private static final String TAG = "UserListPresenter"; private UserListView userListView; public UserListPresenter(UserListView userListView){ this.userListView = userListView; } public void getUserListByAsyncTask(){ Log.d(TAG,"getUserListByAsyncTask"); new GetUserListTask(userListView.getContext()).execute(); } @Override public void resume() { // TODO Auto-generated method stub } @Override public void pause() { // TODO Auto-generated method stub } @Override public void destroy() { // TODO Auto-generated method stub } private void showViewLoading() { userListView.showLoading(); } private void hideViewLoading() { userListView.hideLoading(); } private void showViewLoadingProgress(Integer...values) { userListView.showLoadingProgress(values[0]); } private void hideViewLoadingProgress() { userListView.hideLoadingProgress(); } private void showViewRetry() { userListView.showRetry(); } private void hideViewRetry() { userListView.hideRetry(); } private void showUsersCollectionInView(Collection<UserEntity> usersCollection) { //transform datas ... //show view this.userListView.viewUserList(usersCollection); } private void showErrorMessage(String errorMessage) { userListView.showError(errorMessage); } private class GetUserListTask extends AsyncTask<Void, Integer, UserListEntity> { private Context mContext; public GetUserListTask(Context context){ mContext = context; } @Override protected UserListEntity doInBackground(Void... arg0) { // TODO Auto-generated method stub Log.d(TAG,"GetUserListTask doInBackground"); int level = 0; while(level < 100){ //模拟进度条 publishProgress(level++); try { Thread.sleep(20); } catch (Exception e) { // TODO: handle exception } } return new DataManager(mContext).getUsers(null); } @Override protected void onPostExecute(UserListEntity users) { // TODO Auto-generated method stub super.onPostExecute(users); Log.d(TAG,"GetUserListTask onPostExecute,userListView:" + userListView); hideViewRetry(); hideViewLoading(); userListView.viewUserList(users.getUserList()); } @Override protected void onPreExecute() { // TODO Auto-generated method stub super.onPreExecute(); Log.d(TAG,"GetUserListTask onPreExecute,userListView:" + userListView); hideViewRetry(); showViewLoading(); showViewLoadingProgress(0); } @Override protected void onCancelled() { // TODO Auto-generated method stub super.onCancelled(); hideViewRetry(); hideViewLoading(); } @Override protected void onProgressUpdate(Integer... values) { // TODO Auto-generated method stub super.onProgressUpdate(values); showViewLoadingProgress(values[0]); Log.d(TAG,"GetUserListTask onProgressUpdate,value:" + values[0]); } } }
点击按钮开始获取数据,可以看到不久后数据更新了。
但是如果在点击按钮后旋转屏幕,则看不到数据更新,因为presenter绑定的是旧的activity对象,数据并没有更新到新的activity上。
解决办法:
1.设置旋转屏幕时不重新创建Activity
在AndroidManifest.xml中activity组件定义中设置android:configChanges属性
<activity android:name=".view.activity.SampleActivity" android:label="SampleActivity" android:configChanges="orientation|screenSize|keyboardHidden" />
旋转屏幕后不会调用Activity的onDestroy方法。
可以看到数据更新成功,这是比较简单的方法。
2.将Presenter对象绑定到新的Activity对象
使用Application对象作为桥梁,在创建新activity前将presenter对象缓存,在新activity的onCreate方法中将presenter对象重新绑定。
Application代码:
public class MyApplication extends Application { public static final String PRESENTER_CACHE_KEY = "presenter_cache_key"; private int mPresenterCacheIndex = 0; private HashMap<String,Presenter> mPresenterCacheMap = new HashMap<String,Presenter>(); /** * add presenter to cache map * @param presenter * @return cache key of the presenter */ public synchronized String addPresenterToCache(Presenter presenter){ String key = Integer.toString(mPresenterCacheIndex); mPresenterCacheMap.put(key, presenter); mPresenterCacheIndex++; return key; } /** * get presenter from cache according to given key * @param key * @return */ public synchronized Presenter getPresenterFromCache(String key){ return mPresenterCacheMap.get(key); } public synchronized void removePresenterFromCache(String key){ mPresenterCacheMap.remove(key); } public synchronized int getPresenterCacheSize(){ return mPresenterCacheMap.size(); } }
记得在AndroidManifest.xml中设置这个application对象:
<application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" android:name=".app.MyApplication" >
Presenter中添加重新绑定view接口:
public void resetView(UserListView userListView){ this.userListView = userListView; }
Activity中添加如下代码:
@Override protected void onSaveInstanceState(Bundle outState) { // TODO Auto-generated method stub super.onSaveInstanceState(outState); Log.d(TAG,"onSaveInstanceState"); //缓存presenter对象 String key = ((MyApplication)getApplication()).addPresenterToCache(mPresenter); outState.putString(MyApplication.PRESENTER_CACHE_KEY, key); }
在onCreate方法添加:
if (savedInstanceState != null) { //获取缓存的presenter对象,绑定到当前activity上 mPresenterCacheKey = savedInstanceState.getString(MyApplication.PRESENTER_CACHE_KEY); mPresenter = (UserListPresenter)((MyApplication)getApplication()).getPresenterFromCache(mPresenterCacheKey); mPresenter.resetView(this); }else { mPresenter = new UserListPresenter(this); }
这样就可以看到数据更新成功了。
由于旧的activity对象没有被引用,可以愉快地被回收。
不过这里还没结束,还要考虑让presenter对象愉快地被回收。
@Override protected void onDestroy() { // TODO Auto-generated method stub super.onDestroy(); Log.d(TAG,"onDestroy"); mPresenter.destroy(); if (mPresenterCacheKey != null) { ((MyApplication)getApplication()).removePresenterFromCache(mPresenterCacheKey); } mPresenter = null; //查看缓存对象数量 int size = ((MyApplication)getApplication()).getPresenterCacheSize(); Log.d(TAG,"onDestroy,presenter cache size:" + size); }
不管屏幕旋转多少次,缓存里面只有一个presenter对象,当最后一次activity退出时,缓存被清空,presenter对象也就能被回收。
相关文章推荐
- 技术博客之路
- flyme os 插桩红米2 教程
- Android之应用程序内存优化
- android内存泄露分析
- Android NDK 配置
- Android基础之Touch事件和手势处理
- Android开发学习之路--MAC下Android Studio开发环境搭建
- Android开发学习之路--MAC下Android Studio开发环境搭建
- Activity、Task、应用和进程
- Android so文件生成
- Android笔记 - Binder之处理注册Service组件请求
- MAC 安装 Android studio 教程
- Android图片处理之Glide使用大全
- Android API Level在11前后及16之后时Notification的不同用法
- android根据内容动态更改TextView的字体大小
- 公共技术点之 Android 动画基础
- android文件路径操作详解
- Android类似设置列表分类显示
- Android数据储存之File
- ScrollView嵌套ListView