Текст книги "Разработка Android-приложений в деталях"
Автор книги: Тимур Машнин
Жанр: Компьютеры: прочее, Компьютеры
Возрастные ограничения: +12
сообщить о неприемлемом содержимом
Текущая страница: 10 (всего у книги 20 страниц)
Activity + IntentService + ResultReceiver
Использование класса ResultReceiver еще один способ получения данных из сервиса для их обработки в методе onReceiveResult класса ResultReceiver.
Использование класса ResultReceiver аналогично использованию объекта PendingIntent.
В методе onCreate активности создается объект класса ResultReceiver, который передается в объект Intent, запускающий сервис.
В сервисе объект ResultReceiver извлекается, в него записываются данные, и он отсылается обратно в активность методом send. После этого системой вызывается метод onReceiveResult объекта ResultReceiver, в котором записанные в сервисе данные извлекаются.
Код активности будет иметь следующий вид:
package com. application;
import android. os. Handler;
import android.content.Intent;
import android. os. Bundle;
import android.support. v7.app. AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
import android. widget. TextView;
public class MainActivity extends AppCompatActivity {
public static final String EXTRA_PARAM = "com.application.extra.PARAM»;
public final static String PARAM_RESULT = «Result»;
public final static int RESULT_CODE = 1;
public WebReceiver webReceiver;
@Override
protected void onCreate (Bundle savedInstanceState) {
super. onCreate (savedInstanceState);
setContentView(R.layout.activity_main);
webReceiver = new WebReceiver (new Handler ());
webReceiver.setReceiver (new WebReceiver.Receiver () {
@Override
public void onReceiveResult (int resultCode, Bundle resultData) {
if (resultCode == RESULT_CODE) {
String responseString=resultData.getString(MainActivity.PARAM_RESULT);
TextView textView = (TextView) findViewById(R.id.textView);
textView.setText (responseString);
}
}
});
Intent intent = new Intent(MainActivity.this, WebIntentService.class);
intent. putExtra (EXTRA_PARAM, "http://api.openweathermap.org/data/2.5/weather?q=London,uk");
intent. putExtra («receiver», webReceiver);
startService (intent);
}
public static class WebReceiver extends android.os.ResultReceiver {
public interface Receiver {
public void onReceiveResult (int resultCode, Bundle resultData);
}
private Receiver receiver;
public WebReceiver (android. os. Handler handler) {
super (handler);
}
public void setReceiver (Receiver receiver) {
this.receiver = receiver;
}
@Override
protected void onReceiveResult (int resultCode, Bundle resultData) {
if (receiver!= null) {
receiver. onReceiveResult (resultCode, resultData);
}
}
}
@Override
public boolean onCreateOptionsMenu (Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected (MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest. xml.
int id = item.getItemId ();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super. onOptionsItemSelected (item);
}
}
Здесь ResultReceiver представлен внутренним классом WebReceiver активности, экземпляр которого создается в методе onCreate. Затем экземпляр ResultReceiver передается в объект Intent, запускающий сервис.
Код IntentService при этом будет иметь следующий вид:
package com. application;
import android.app.IntentService;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.Context;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android. os. Bundle;
import android.os.ResultReceiver;
import org. json. JSONObject;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net. URL;
import java.text.SimpleDateFormat;
import java.util.Calendar;
public class WebIntentService extends IntentService {
public WebIntentService () {
super («WebIntentService»);
}
@Override
protected void onHandleIntent (Intent intent) {
if (intent!= null) {
final String param = intent.getStringExtra (MainActivity. EXTRA_PARAM);
ResultReceiver receiver = intent.getParcelableExtra («receiver»);
handleAction (param, receiver);
}
}
/**
* Handle action in the provided background thread with the provided
* parameters.
*/
private void handleAction (String param, ResultReceiver receiver) {
ConnectivityManager connMgr = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connMgr.getActiveNetworkInfo ();
if (networkInfo!= null && networkInfo.isConnected () && networkInfo.isAvailable ()) {
InputStream is = null;
URL url = null;
try {
url = new URL (param);
HttpURLConnection conn = (HttpURLConnection) url. openConnection ();
conn.setReadTimeout (10000 /* milliseconds */);
conn.setConnectTimeout (15000 /* milliseconds */);
conn.setRequestMethod («GET»);
conn.setDoInput (true);
conn.connect ();
int response = conn.getResponseCode ();
is = conn.getInputStream ();
// Convert the InputStream into a string
BufferedReader br = null;
StringBuilder sb = new StringBuilder ();
String line;
br = new BufferedReader (new InputStreamReader (is));
while ((line = br.readLine ())!= null) {
sb. append (line);
}
String contentAsString=sb.toString ();
is.close ();
conn. disconnect ();
JSONObject reader = new JSONObject (contentAsString);
JSONObject main = reader.getJSONObject («main»);
String temp = main.getString («temp»);
Calendar c = Calendar.getInstance ();
SimpleDateFormat df = new SimpleDateFormat («yyyy-MM-dd HH: mm: ss»);
String formattedDate = df.format(c.getTime ());
String result=formattedDate + " temp: " +temp;
Bundle bundle = new Bundle ();
bundle.putString(MainActivity.PARAM_RESULT, result);
receiver.send(MainActivity.RESULT_CODE, bundle);
Context context = this;
SharedPreferences sharedPref = context.getSharedPreferences("com.application.PREFERENCE_FILE_KEY",Context.MODE_PRIVATE);
SharedPreferences. Editor editor = sharedPref. edit ();
editor. putString («temp», temp);
editor. putString («date», formattedDate);
editor.commit ();
} catch (Exception e) {
e.printStackTrace ();
}
} else {
Context context = this;
SharedPreferences sharedPref = context.getSharedPreferences("com.application.PREFERENCE_FILE_KEY», Context.MODE_PRIVATE);
String date=sharedPref.getString («date», «unknown date»);
String temp=sharedPref.getString («temp», «unknown temp»);
String result=date + " temp: " +temp;
Bundle bundle = new Bundle ();
bundle.putString(MainActivity.PARAM_RESULT, result);
receiver.send(MainActivity.RESULT_CODE, bundle);
}
}
}
Здесь, при запуске сервиса, проверяется наличие соединения с Интернетом, затем получаются данные в JSON-формате по указанному URL адресу.
Далее необходимые данные извлекаются и передаются в объект Bundle в виде строки.
Объект Bundle передается в объект ResultReceiver, который отправляется обратно в создавшую его активность методом send.
Извлеченные данные записываются в настройки SharedPreferences.
Если же соединение с Интернетом отсутствует, тогда данные извлекаются из настроек SharedPreferences и передаются в объект Bundle в виде строки.
Для отключения соединения с Интернетом в эмуляторе можно нажать F8.
Activity + Service + Binder
При создании Bound-сервиса, сервис запускается из активности методом bindService. При этом в сервисе вызывается метод onBind, возвращающий объект IBinder, который обеспечивает доступ к экземпляру сервиса и соответственно методам сервиса.
Методы сервиса при том выполняются в основном потоке.
Если же требуется выполнение задачи в фоновом потоке и возврат результата из него обратно в активность, можно передать сервису объект Handler для отправки и получения данных из фонового потока.
Код Bound-сервиса будет следующим:
package com. application;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Binder;
import android. os. Bundle;
import android. os. IBinder;
import android. os. Handler;
import android.os.Message;
import org. json. JSONObject;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net. URL;
import java.text.SimpleDateFormat;
import java.util.Calendar;
public class WebBoundService extends Service {
private final IBinder binder = new LocalBinder ();
public WebBoundService () {
}
@Override
public IBinder onBind (Intent intent) {
return binder;
}
public class LocalBinder extends Binder {
WebBoundService getService () {
return WebBoundService.this;
}
}
public void getResult (String str, Handler hlr) {
final Handler handler=hlr;
final String urlString=str;
final Context context = this;
new Thread (new Runnable () {
public void run () {
try {
ConnectivityManager connMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connMgr.getActiveNetworkInfo ();
if (networkInfo!= null && networkInfo.isConnected () && networkInfo.isAvailable ()) {
InputStream is = null;
URL url = null;
url = new URL (urlString);
HttpURLConnection conn = (HttpURLConnection) url. openConnection ();
conn.setReadTimeout (10000 /* milliseconds */);
conn.setConnectTimeout (15000 /* milliseconds */);
conn.setRequestMethod («GET»);
conn.setDoInput (true);
conn.connect ();
is = conn.getInputStream ();
// Convert the InputStream into a string
BufferedReader br = null;
StringBuilder sb = new StringBuilder ();
String line;
br = new BufferedReader (new InputStreamReader (is));
while ((line = br.readLine ())!= null) {
sb. append (line);
}
String contentAsString = sb.toString ();
is.close ();
conn. disconnect ();
JSONObject reader = new JSONObject (contentAsString);
JSONObject main = reader.getJSONObject («main»);
String temp = main.getString («temp»);
Calendar c = Calendar.getInstance ();
SimpleDateFormat df = new SimpleDateFormat («yyyy-MM-dd HH: mm: ss»);
String formattedDate = df.format(c.getTime ());
String msg = formattedDate + " temp: " + temp;
Message msgObj = handler. obtainMessage ();
Bundle b = new Bundle ();
b. putString («message», msg);
msgObj.setData (b);
handler.sendMessage (msgObj);
SharedPreferences sharedPref = context.getSharedPreferences("com.application.PREFERENCE_FILE_KEY», Context.MODE_PRIVATE);
SharedPreferences. Editor editor = sharedPref. edit ();
editor. putString («temp», temp);
editor. putString («date», formattedDate);
editor.commit ();
} else {
SharedPreferences sharedPref = context.getSharedPreferences("com.application.PREFERENCE_FILE_KEY», Context.MODE_PRIVATE);
String date = sharedPref.getString («date», «unknown date»);
String temp = sharedPref.getString («temp», «unknown temp»);
String msg=date + " temp: " +temp;
Message msgObj = handler. obtainMessage ();
Bundle b = new Bundle ();
b. putString («message», msg);
msgObj.setData (b);
handler.sendMessage (msgObj);
}
} catch (Exception e) {
e.printStackTrace ();
}
}
}).start ();
}
}
Здесь, в метод сервиса передается URL-адрес для получения данных и объект Handler для отправки полученных данных.
В фоновом потоке метода проверяется наличие соединения с Интернетом, затем получаются данные в JSON-формате по указанному URL адресу.
Далее необходимые данные извлекаются и передаются в объект Handler в виде строки.
Извлеченные данные записываются в настройки SharedPreferences.
Если же соединение с Интернетом отсутствует, тогда данные извлекаются из настроек SharedPreferences и передаются в объект Handler.
Код активности, запускающей сервис, будет следующим:
package com. application;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android. os. Bundle;
import android. os. Handler;
import android. os. IBinder;
import android.os.Message;
import android.support. v7.app. AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
import android. widget. TextView;
public class MainActivity extends AppCompatActivity {
private ServiceConnection sConn;
private boolean bound = false;
private WebBoundService service;
@Override
protected void onCreate (Bundle savedInstanceState) {
super. onCreate (savedInstanceState);
setContentView(R.layout.activity_main);
sConn = new ServiceConnection () {
public void onServiceConnected (ComponentName name, IBinder binder) {
WebBoundService. LocalBinder localBinder = (WebBoundService. LocalBinder) binder;
service = localBinder.getService ();
Handler handler = new Handler () {
@Override
public void handleMessage (Message msg) {
super. handleMessage (msg);
Bundle b = msg.getData ();
String responseString=b.getString («message»);
TextView textView = (TextView) findViewById(R.id.textView);
textView.setText (responseString);
}
};
http://api.openweathermap.org/data/2.5/weather?q=London,uk",tResult (»service.ge handler);
bound = true;
}
public void onServiceDisconnected (ComponentName name) {
bound = false;
}
};
Intent intent = new Intent(MainActivity.this, WebBoundService.class);
bindService (intent, sConn, this.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy () {
super. onDestroy ();
if (bound) {
unbindService (sConn);
bound = false;
}
}
@Override
public boolean onCreateOptionsMenu (Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected (MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest. xml.
int id = item.getItemId ();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super. onOptionsItemSelected (item);
}
}
Здесь в методе handleMessage объекта Handler, отправленные сервисом данные извлекаются и отображаются.
Код манифеста AndroidManifest. xml:
<?xml version=«1.0» encoding=«utf-8»? >
<manifest xmlns: android="http://schemas.android.com/apk/res/android"
package="com.tmsoftstudio. application»>
<uses-permission android:name="android.permission.INTERNET» />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE» />
<application
android: allowBackup=«true»
android: icon="@mipmap/ic_launcher»
android: label="@string/app_name»
android: theme="@style/AppTheme»>
<activity
android:name=".MainActivity»
android: label="@string/app_name»>
<intent-filter>
<action android:name="android.intent.action.MAIN» />
<category android:name="android.intent.category.LAUNCHER» />
</intent-filter>
</activity>
<service
android: name=». WebBoundService»
android: enabled=«true»
android: exported=«true»>
</service>
</application>
</manifest>
Activity + Service + Messenger
Класс Messenger обеспечивает еще один способ обмена данными между активностью и Bound-сервисом, только при этом находящимися в разных процессах.
К объекту Messenger прикрепляется обработчик Handler, отвечающий за обработку поступающих сообщений.
В классе активности для отправки сообщения сервису, в методе обратного вызова onServiceConnected создается объект Messenger на основе объекта IBinder, передаваемого из сервиса.
После того создается объект Message, в который записываются данные.
В объект Message записывается объект Messenger, к которому прикреплен обработчик Handler, отвечающий за обработку сообщений от сервиса.
Объект Message отправляется сервису с помощью объекта Messenger, созданного на основе объекта IBinder.
В сервисе вызывается метод handleMessage обработчика Handler, связанного с объектом Messenger, из которого получается объект IBinder, отправляемый активности.
В методе handleMessage из сообщения извлекается объект Messenger, с помощью которого данные отправляются обратно активности.
Код активности будет следующим:
package com. application;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android. os. Bundle;
import android. os. Handler;
import android. os. IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.support. v7.app. AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
import android. widget. TextView;
public class MainActivity extends AppCompatActivity {
private ServiceConnection sConn;
private boolean bound = false;
private Messenger mMessenger;
@Override
protected void onCreate (Bundle savedInstanceState) {
super. onCreate (savedInstanceState);
setContentView(R.layout.activity_main);
sConn = new ServiceConnection () {
public void onServiceConnected (ComponentName name, IBinder binder) {
mMessenger = new Messenger (binder);
Message msg = Message. obtain (null, WebBoundService.MSG_TO_SERVICE);
msg.replyTo = new Messenger (new ActivityHandler ());
Bundle bundle = new Bundle ();
bundle. putString («url», "http://api.openweathermap.org/data/2.5/weather?q=London,uk");
msg.setData (bundle);
try {
mMessenger.send (msg);
} catch (RemoteException e) {
e.printStackTrace ();
}
bound = true;
}
public void onServiceDisconnected (ComponentName name) {
mMessenger = null;
bound = false;
}
};
Intent intent = new Intent(MainActivity.this, WebBoundService.class);
bindService (intent, sConn, this.BIND_AUTO_CREATE);
}
class ActivityHandler extends Handler {
@Override
public void handleMessage (Message msg) {
switch (msg. what) {
case WebBoundService.MSG_FROM_SERVICE: {
Bundle b = msg.getData ();
String responseString=b.getString («respData»);
TextView textView = (TextView) findViewById(R.id.textView);
textView.setText (responseString);
}
break;
default:
super. handleMessage (msg);
}
}
}
@Override
protected void onDestroy () {
super. onDestroy ();
if (bound) {
unbindService (sConn);
bound = false;
}
}
@Override
public boolean onCreateOptionsMenu (Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected (MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest. xml.
int id = item.getItemId ();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super. onOptionsItemSelected (item);
}
}
Код Bound-сервиса:
package com. application;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android. os. Bundle;
import android. os. IBinder;
import android. os. Handler;
import android.os.Message;
import android.os.Messenger;
import org. json. JSONObject;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net. URL;
import java.text.SimpleDateFormat;
import java.util.Calendar;
public class WebBoundService extends Service {
final Context context = this;
static final int MSG_TO_SERVICE = 1;
static final int MSG_FROM_SERVICE = 2;
public WebBoundService () {
}
Messenger mMessenger = new Messenger (new ServiceHandler ());
// Incoming messages Handler
class ServiceHandler extends Handler {
@Override
public void handleMessage (Message msg) {
switch (msg. what) {
case MSG_TO_SERVICE: {
final Messenger messenger=msg.replyTo;
Bundle bundle = msg.getData ();
final String urlString = (String) bundle.get («url»);
new Thread (new Runnable () {
public void run () {
try {
ConnectivityManager connMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connMgr.getActiveNetworkInfo ();
if (networkInfo!= null && networkInfo.isConnected () && networkInfo.isAvailable ()) {
InputStream is = null;
URL url = null;
url = new URL (urlString);
HttpURLConnection conn = (HttpURLConnection) url. openConnection ();
conn.setReadTimeout (10000 /* milliseconds */);
conn.setConnectTimeout (15000 /* milliseconds */);
conn.setRequestMethod («GET»);
conn.setDoInput (true);
conn.connect ();
is = conn.getInputStream ();
// Convert the InputStream into a string
BufferedReader br = null;
StringBuilder sb = new StringBuilder ();
String line;
br = new BufferedReader (new InputStreamReader (is));
while ((line = br.readLine ())!= null) {
sb. append (line);
}
String contentAsString = sb.toString ();
is.close ();
conn. disconnect ();
JSONObject reader = new JSONObject (contentAsString);
JSONObject main = reader.getJSONObject («main»);
String temp = main.getString («temp»);
Calendar c = Calendar.getInstance ();
SimpleDateFormat df = new SimpleDateFormat («yyyy-MM-dd HH: mm: ss»);
String formattedDate = df.format(c.getTime ());
String data = formattedDate + " temp: " + temp;
Message resp = Message. obtain (null, MSG_FROM_SERVICE);
Bundle bResp = new Bundle ();
bResp. putString («respData», data);
resp.setData (bResp);
messenger.send (resp);
SharedPreferences sharedPref = context.getSharedPreferences("com.application.PREFERENCE_FILE_KEY», Context.MODE_PRIVATE);
SharedPreferences. Editor editor = sharedPref. edit ();
editor. putString («temp», temp);
editor. putString («date», formattedDate);
editor.commit ();
} else {
SharedPreferences sharedPref = context.getSharedPreferences("com.application.PREFERENCE_FILE_KEY», Context.MODE_PRIVATE);
String date = sharedPref.getString («date», «unknown date»);
String temp = sharedPref.getString («temp», «unknown temp»);
String data = date + " temp: " + temp;
Message resp = Message. obtain (null, MSG_FROM_SERVICE);
Bundle bResp = new Bundle ();
bResp. putString («respData», data);
resp.setData (bResp);
messenger.send (resp);
}
} catch (Exception e) {
e.printStackTrace ();
}
}
}).start ();
}
break;
default:
super. handleMessage (msg);
}
}
}
@Override
public IBinder onBind (Intent intent) {
return mMessenger.getBinder ();
}
}
Код манифеста:
<?xml version=«1.0» encoding=«utf-8»? >
<manifest xmlns: android="http://schemas.android.com/apk/res/android"
package="com.tmsoftstudio. application»>
<uses-permission android:name="android.permission.INTERNET» />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE» />
<application
android: allowBackup=«true»
android: icon="@mipmap/ic_launcher»
android: label="@string/app_name»
android: theme="@style/AppTheme»>
<activity
android:name=".MainActivity»
android: label="@string/app_name»>
<intent-filter>
<action android:name="android.intent.action.MAIN» />
<category android:name="android.intent.category.LAUNCHER» />
</intent-filter>
</activity>
<service
android: name=». WebBoundService»
android: enabled=«true»
android: exported=«true»
android: process=":webservice»
>
</service>
</application>
</manifest>
Здесь атрибут android: process=":webservice» указывает на запуск сервиса в отдельном процессе.
Правообладателям!
Это произведение, предположительно, находится в статусе 'public domain'. Если это не так и размещение материала нарушает чьи-либо права, то сообщите нам об этом.