您的位置:首页 > 移动开发 > Android开发

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代码:

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对象也就能被回收。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: