Звоните в сеть api, так как активность заканчивается
У меня есть экран настроек в моем приложении, и я хочу сохранить пользовательские настройки на сервере, но я не хочу, чтобы в настройках была какая-либо кнопка обновления, я надеялся сделать этот вызов api обновления, когда действие заканчивается, может быть, я Можно вызвать API в onPause(), проблема в том, что если API возвращает больше времени, чем ожидалось, для возврата, то у меня будет сетевой вызов, выполняемый без активности, что, как я знаю, является утечкой памяти. Если я попытаюсь выполнить этот сетевой вызов в главном потоке, и этот вызов займет слишком много времени, я получу ANR. Как я могу добиться такого поведения в моем приложении. Я использую retrofit2 с rxjava2, чтобы сделать вызовы API.
2 ответа
Если вам нужен ответ (например, Toast, чтобы сообщить пользователю, что настройки успешно сохранены), используйте IntentService, в противном случае Service. Для подробного описания различий см. Сервис против IntentService. Для реализации (с хорошим примером) взгляните на: https://developer.android.com/guide/components/services и https://developer.android.com/guide/components/broadcasts (для реализации приемника вещания чтобы обработать результат)
Вот простое приложение. В основной деятельности мы переходим к SecondActivity. Там мы устанавливаем некоторые значения, запускаем сервис для их обработки и завершаем действие. Когда все готово, MainActivity обновляется автоматически.
AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="net.sytes.csongi.servicetest">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".SecondActivity">
</activity>
<service
android:name="services.MyIntentService"
android:exported="false"
>
</service>
</application>
</manifest>
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/main_result_txt"
android:text="Test"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/main_open_intent_btn"
android:text="start"
android:layout_gravity="center"/>
</LinearLayout>
</android.support.design.widget.CoordinatorLayout>
activity_second.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".SecondActivity">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/second_edit_01_txt"
android:hint="Enter some text here"/>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/second_edit_02_txt"
android:hint="another text here"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/second_start_process_btn"
android:text="start processing"
android:layout_gravity="center"/>
</LinearLayout>
MainActivity.java:
package net.sytes.csongi.servicetest;
import android.content.*;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.Button;
import android.widget.TextView;
import butterknife.BindView;
import butterknife.ButterKnife;
import services.MyIntentService;
public class MainActivity extends AppCompatActivity {
private static final String TAG=MainActivity.class.getSimpleName();
private BroadcastReceiver mBroadcastReceiver;
private IntentFilter mIntentFilter;
@BindView(R.id.main_open_intent_btn)
Button mOpenIntentBtn;
@BindView(R.id.main_result_txt)
TextView mResultTxt;
public static final String ACTION_MAIN_ACTIVITY = "ACTION_MAIN_ACTIVITY";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
mIntentFilter=new IntentFilter();
mIntentFilter.addAction(ACTION_MAIN_ACTIVITY);
mBroadcastReceiver=new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "onReceive:::: called");
String action=intent.getAction();
if(action!=null&& ACTION_MAIN_ACTIVITY.equals(action)){
processReceivedIntent(intent);
}
}
};
registerReceiver(mBroadcastReceiver,mIntentFilter);
mOpenIntentBtn.setOnClickListener(v->
{
Intent startSecondActivity=new Intent(this,SecondActivity.class);
startActivity(startSecondActivity);
});
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy:::: called, unregistering Receiver");
unregisterReceiver(mBroadcastReceiver);
}
private void processReceivedIntent(Intent intent) {
Log.d(TAG, "processReceivedIntent:::: called");
StringBuilder builder=new StringBuilder("The values recieved: \n");
String returnedString=intent.getStringExtra(MyIntentService.EXTRA_TO_PROCESS);
builder.append(returnedString);
mResultTxt.setText(builder.toString());
}
}
SecondActivity.java:
package net.sytes.csongi.servicetest;
import android.content.ContentValues;
import android.os.Bundle;
import android.support.annotation.StringDef;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.Button;
import android.widget.EditText;
import butterknife.BindView;
import butterknife.ButterKnife;
import services.MyIntentService;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import static net.sytes.csongi.servicetest.SecondActivity.ValuesToSend.EDIT_TEXT_ONE;
import static net.sytes.csongi.servicetest.SecondActivity.ValuesToSend.EDIT_TEXT_TWO;
public class SecondActivity extends AppCompatActivity {
private static final String TAG=SecondActivity.class.getSimpleName();
@BindView(R.id.second_edit_01_txt)
EditText mSecondEditOne;
@BindView(R.id.second_edit_02_txt)
EditText mSecondEditTwo;
@BindView(R.id.second_start_process_btn)
Button mStartProcess;
@Retention(RetentionPolicy.SOURCE)
@StringDef({EDIT_TEXT_ONE,
EDIT_TEXT_TWO})
public @interface ValuesToSend {
/**
* edit text key for textField_01
*/
String EDIT_TEXT_ONE = "EDIT_TEXT_ONE";
/**
* edit text key for textField_02;
*/
String EDIT_TEXT_TWO = "EDIT_TEXT_TWO";
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
ButterKnife.bind(this);
mStartProcess.setOnClickListener(v -> {
startProcess();
});
}
private void startProcess() {
Log.d(TAG, "startProcess:::: called");
finish();
}
@Override
protected void onStop() {
super.onStop();
Log.d(TAG, "onStop:::: called");
ContentValues valuesToSend=new ContentValues();
valuesToSend.put(EDIT_TEXT_ONE,mSecondEditOne.getText().toString());
valuesToSend.put(EDIT_TEXT_TWO,mSecondEditTwo.getText().toString());
Log.d(TAG, "onStop:::: contentValues size="+valuesToSend.size());
Bundle toSend=new Bundle();
toSend.putParcelable(MyIntentService.BUNDLE_CONTENT_VALUES_KEY,valuesToSend);
MyIntentService.startActionProcess(this,toSend);
}
}
MyIntentService.java:
package services;
import android.app.IntentService;
import android.content.ContentValues;
import android.content.Intent;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import net.sytes.csongi.servicetest.MainActivity;
import java.util.Iterator;
import java.util.Set;
/**
* An {@link IntentService} subclass for handling asynchronous task requests in
* a service on a separate handler thread.
* <p>
* helper methods.
*/
public class MyIntentService extends IntentService {
private static final String TAG=MyIntentService.class.getSimpleName();
// IntentService can perform, e.g. ACTION_FETCH_NEW_ITEMS
public static final String ACTION_PROCESS = "services.action.PROCESS";
public static final String BUNDLE_TO_PROCESS="BUNDLE_TO_PROCESS";
public static final String BUNDLE_CONTENT_VALUES_KEY = "BUNDLE_CONTENT_VALUES_KEY";
public static final String EXTRA_TO_PROCESS = "EXTRA_TO_PROCESS";
public MyIntentService() {
super("MyIntentService");
Log.d(TAG, "MyIntentService:::: instantiated");
}
/**
* Starts this service to perform action Foo with the given parameters. If
* the service is already performing a task this action will be queued.
*
* @see IntentService
*/
public static void startActionProcess(Context context, Bundle bundle) {
Log.d(TAG, "startActionProcess:::: called");
Intent intent = new Intent(context, MyIntentService.class);
intent.setAction(ACTION_PROCESS);
intent.putExtra(BUNDLE_TO_PROCESS, bundle);
context.startService(intent);
}
@Override
protected void onHandleIntent(Intent intent) {
Log.d(TAG, "onHandleIntent:::: called");
if (intent != null) {
final String action = intent.getAction();
if (ACTION_PROCESS.equals(action)) {
final Bundle bundleToProcess = intent.getBundleExtra(BUNDLE_TO_PROCESS);
handleAction(bundleToProcess);
}
}
}
private void handleAction(Bundle bundleToProcess) {
// here we run the long process
Log.d(TAG, "handleAction:::: called");
ContentValues contentValues= bundleToProcess.getParcelable(BUNDLE_CONTENT_VALUES_KEY);
StringBuilder builder=new StringBuilder("Processed values are:\n");
int numberOfValues=contentValues.size();
Set<String> valuesKeySet=contentValues.keySet();
Iterator<String> iterator=valuesKeySet.iterator();
while(iterator.hasNext()){
builder.append(contentValues.getAsString(iterator.next())+"\n");
try{ // simulating latency
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// send result to appropriate activity
Intent resultIntent=new Intent();
resultIntent.setAction(MainActivity.ACTION_MAIN_ACTIVITY);
resultIntent.putExtra(EXTRA_TO_PROCESS,builder.toString());
sendBroadcast(resultIntent);
}
}
Надеюсь, я смогу вам помочь.
Вы можете создать AsyncTask для сети, так как doInBackground()
создает фоновый поток, который вам нужен, чтобы избежать создания сетей на MainThread. Кроме того, остановка активности не останавливает AsyncTask.