Текст книги "Разработка Android-приложений в деталях"
Автор книги: Тимур Машнин
Жанр: Компьютеры: прочее, Компьютеры
Возрастные ограничения: +12
сообщить о неприемлемом содержимом
Текущая страница: 11 (всего у книги 20 страниц)
Activity + Service + AIDL
AIDL интерфейс используется, если взаимодействие с сервисом производится из разных приложений и ваш сервис должен обеспечивать многопоточность.
Для создания AIDL интерфейса в Android Studio нажмем правой кнопкой мышеи на модуле и выберем New | AIDL | AIDL File.
В результате, в каталоге src/main модуля будет создана папка aidl с файлом интерфейса, на основе которого при сборке модуля в каталоге build/generated будет сгенерирована Java-реализация AIDL интерфейса.
Сгенерированная Java-реализация AIDL интерфейса представляет собой класс-заглушку, который расширяет класс Binder и поэтому может быть возвращен методом onBind Bound-сервиса.
Возвращенный в активность экземпляр AIDL-класса может содержать методы, вызываемые активностью.
Код AIDL-интерфейса:
package com. application;
interface IAidlInterface {
String getResult (String url);
}
Код Bound-сервиса:
package com. application;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android. os. IBinder;;
import android.os.RemoteException;
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;
import java.util.concurrent.Callable;
import java.util.concurrent. FutureTask;
import java.util.concurrent.RunnableFuture;
public class WebBoundService extends Service {
public WebBoundService () {
}
@Override
public IBinder onBind (Intent intent) {
return new IAidlInterface.Stub () {
public String getResult (String url) throws RemoteException {
final String urlString=url;
String resp=«»;
RunnableFuture f = new FutureTask (new Callable <String> () {
@Override
public String call () throws Exception {
String data=«»;
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 ());
data = formattedDate + " temp: " + temp;
return data;
} else {
return data;
}
}
});
new Thread(f).start ();
try {
resp= (String) f.get ();
} catch (Exception e) {
e.printStackTrace ();
}
return resp;
}
};
}
}
Здесь в реализации метода AIDL-интерфейса данные получаются из Интернета в отдельном потоке.
Для возврата данных из отдельного потока используется класс FutureTask.
Код активности:
package com. application;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android. os. Bundle;
import android. os. IBinder;
import android.os.RemoteException;
import android.support. v7.app. AppCompatActivity;
import android. util. Log;
import android.view.Menu;
import android.view.MenuItem;
import android. widget. TextView;
public class MainActivity extends AppCompatActivity {
private final Context context=this;
private ServiceConnection sConn;
private boolean bound = false;
private IAidlInterface 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) {
service = IAidlInterface.Stub.asInterface ((IBinder) binder);
String data=«»;
try {
data = http://api.openweathermap.org/data/2.5/weather?q=London,uk");tResult (»service.ge
} catch (RemoteException e) {
e.printStackTrace ();
}
if (!data. equals (»»)) {
SharedPreferences sharedPref = context.getSharedPreferences("com.application.PREFERENCE_FILE_KEY», Context.MODE_PRIVATE);
SharedPreferences. Editor editor = sharedPref. edit ();
editor. putString («data», data);
editor.commit ();
TextView textView = (TextView) findViewById(R.id.textView);
textView.setText (data);
} else {
SharedPreferences sharedPref = context.getSharedPreferences("com.application.PREFERENCE_FILE_KEY», Context.MODE_PRIVATE);
String savedData = sharedPref.getString («data», «unknown data»);
TextView textView = (TextView) findViewById(R.id.textView);
textView.setText (savedData);
}
bound = true;
}
public void onServiceDisconnected (ComponentName name) {
service = null;
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) {
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected (MenuItem item) {
int id = item.getItemId ();
if (id == R.id.action_settings) {
return true;
}
return super. onOptionsItemSelected (item);
}
}
Здесь в методе onServiceConnected при соединении с Bound-сервисом получается экземпляр AIDL-класса, после этого вызывается его метод.
Отображение Toast сообщения из сервиса или потока
Handler handler = new Handler(Looper.getMainLooper ());
handler.post (new Runnable () {
@Override
public void run () {
Toast.makeText(context.getApplicationContext (),«No internet connection",Toast.LENGTH_SHORT).show ();
}
});
Изменение цвета шрифта для всей активности
В файл styles. xml в тэг <style> включим элемент:
<item name=«android: textColor»> @color/colorPrimary </item>
Получить выбранный RadioButton из RadioGroup
RadioGroup radioButtonGroup = (RadioGroup) view.findViewById(R.id.radiogroup);
int radioButtonID = radioButtonGroup.getCheckedRadioButtonId ();
RadioButton radioButton = (RadioButton) radioButtonGroup.findViewById (radioButtonID);
String str = radioButton.getText().toString ();
Сборка APK файла с большим количеством методов в коде
В Gradle файл добавить:
defaultConfig {
multiDexEnabled true
}
dependencies {
compile 'com.android.support: multidex:1.0.0»
}
android {
dexOptions {
incremental true
javaMaxHeapSize «4g»
}
}
В файл манифеста:
<application
android:name="android.support.multidex.MultiDexApplication»>
Получение Google аккаунта пользователя
Intent intent = AccountPicker.newChooseAccountIntent (null, null, new String [] {«com. google»},
true, null, null, null, null);
startActivityForResult (intent, REQUEST_CODE_ACCOUNT);
protected void onActivityResult (final int requestCode, final int resultCode, final Intent data) {
if (requestCode == REQUEST_CODE_ACCOUNT && resultCode == RESULT_OK)
{
String accountName = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
SharedPreferences. Editor editor = mSettings. edit ();
editor. putString («APP_PREFERENCES_ACCOUNT», accountName);
editor.commit ();
}
}
Присоединение слушателя к Button
Button button = (Button) findViewById(R.id.button_id);
button.setOnClickListener (new View. OnClickListener () {
public void onClick (View v) {
// Perform action on click
}
});
Передача изображения в Google App Engine Java Blobstore используя Google Cloud Endpoints
Выбор изображения в галереи:
Button imageButton=(Button)view.findViewById(R.id.change_profile_photo);
imageButton.setOnClickListener (new View. OnClickListener () {
public void onClick (View v) {
Intent photoPickerIntent = new Intent(Intent.ACTION_PICK);
photoPickerIntent.setType («image/*»);
getActivity().startActivityForResult (photoPickerIntent, REQUEST_CODE_PHOTO);
}
});
protected void onActivityResult (final int requestCode, final int resultCode, final Intent data) {
if (requestCode == REQUEST_CODE_PHOTO && resultCode == RESULT_OK) {
Uri selectedImage = data.getData ();
SharedPreferences. Editor editor = mSettings. edit ();
editor. putString («APP_PREFERENCES_PHOTO», selectedImage.toString ());
editor.commit ();
}
}
Кодирование изображения в Base64-строку:
Uri imageUri = Uri.parse(mSettings.getString («APP_PREFERENCES_PHOTO»,»»));
InputStream imageStream = null;
try {
imageStream = getContext().getContentResolver ().openInputStream (imageUri);
} catch (FileNotFoundException e) {
e.printStackTrace ();
}
Bitmap bitmap = BitmapFactory.decodeStream (imageStream);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream ();
bitmap.compress(Bitmap.CompressFormat.JPEG, 10, outputStream);
bitmap.recycle ();
bitmap = null;
byte [] bitmapByte = outputStream.toByteArray ();
outputStream=null;
String stringEncodedImage = Base64.encodeToString (bitmapByte, Base64.DEFAULT);
bitmapByte=null;
Отправка изображения:
private static MyApi myApiService = null;
private MyBean profile=new MyBean ();
profile.setMImage (stringEncodedImage);
ProfileDialogAsyncTask task=new ProfileDialogAsyncTask ();
task. execute (profile);
private static class ProfileDialogAsyncTask extends AsyncTask <MyBean, Integer, MyBean> {
private MyApi myApiService = null;
private ProgressBar pb;
@Override
protected void onPreExecute () {
super. onPreExecute ();
pb = (ProgressBar)context.findViewById(R.id.progress);
pb.setVisibility(View.VISIBLE);
}
@Override
protected void onPostExecute (MyBean myBean) {
super. onPostExecute (myBean);
Toast.makeText(context.getApplicationContext (),«The profile has been saved",Toast.LENGTH_SHORT).show ();
SharedPreferences. Editor editor = mSettings. edit ();
editor. putString («APP_PREFERENCES_PHOTO_URL», myBean.getMImage ());
editor.commit ();
pb.setVisibility(View.INVISIBLE);
}
@Override
protected void onProgressUpdate (Integer… values) {
super. onProgressUpdate (values);
}
@Override
protected MyBean doInBackground (MyBean… params) {
MyBean profile=params [0];
MyBean response=null;
if (myApiService == null) {
MyApi. Builder builder = new MyApi.Builder(AndroidHttp.newCompatibleTransport (),
new AndroidJsonFactory (), null)
.setRootUrl (»https://meet-love-android.appspot.com/_ah/api/")
.setGoogleClientRequestInitializer (new GoogleClientRequestInitializer () {
@Override
public void initialize (AbstractGoogleClientRequest <?> abstractGoogleClientRequest) throws IOException {
abstractGoogleClientRequest.setDisableGZipContent (true);
}
});
myApiService = builder. build ();
}
try {
response=myApiService.setProfile (profile).execute ();
} catch (IOException e) {
}
return response;
}
}
Получение изображения:
import com.google.api.server.spi.config. Api;
import com.google.api.server.spi.config. ApiMethod;
import com.google.api.server.spi.config. ApiNamespace;
import com. google. appengine. api. blobstore. BlobInfo;
import com. google. appengine. api. blobstore. BlobInfoFactory;
import com. google. appengine. api. blobstore. BlobKey;
import com. google. appengine. api. blobstore. BlobstoreService;
import com. google. appengine. api. blobstore. BlobstoreServiceFactory;
import com. google. appengine. api. datastore. DatastoreService;
import com. google. appengine. api. datastore. DatastoreServiceFactory;
import com. google. appengine. api. datastore. Entity;
import com. google. appengine. api. datastore. EntityNotFoundException;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.images.ImagesService;
import com.google.appengine.api.images.ImagesServiceFactory;
import com.google.appengine.api.images.ServingUrlOptions;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net. URL;
import java.util.Iterator;
@Api (
name = «myApi»,
version = «v1»,
namespace = @ApiNamespace (
ownerDomain = "backend.com»,
ownerName = "backend.com»,
packagePath=»»
)
)
public class MyEndpoint {
private DatastoreService datastore = DatastoreServiceFactory.getDatastoreService ();
@ApiMethod (
name = «setProfile»,
httpMethod = ApiMethod.HttpMethod.POST
)
public MyBean setProfile (MyBean profile) {
String accountName=profile.getmAccount ();
Key key = KeyFactory.createKey («User», accountName);
Entity user=null;
try {
user = datastore.get (key);
} catch (EntityNotFoundException e) {
e.printStackTrace ();
}
if (user==null) {
user = new Entity («User», accountName);
}
byte [] imageAsBytes = Base64.decodeBase64(profile.getmImage ());
BlobstoreService blobService = BlobstoreServiceFactory.getBlobstoreService ();
BlobInfoFactory bf = new BlobInfoFactory ();
Iterator itr = bf. queryBlobInfos ();
while (itr. hasNext ()) {
BlobInfo bi = (BlobInfo)itr.next ();
if (bi.getFilename().equals(accountName+".jpeg»)) {
blobService.delete(bi.getBlobKey ());
}
}
String uploadUrl = blobService.createUploadUrl (»/blob/upload»);
HttpEntity requestEntity = MultipartEntityBuilder.create ()
.addBinaryBody («file», imageAsBytes, ContentType.create («image/jpg»), accountName+".jpeg»)
.build ();
try {
URL url = new URL (uploadUrl);
HttpURLConnection connection = (HttpURLConnection) url. openConnection ();
connection.setDoOutput (true);
connection.setRequestMethod («POST»);
connection.addRequestProperty («Content-length», requestEntity.getContentLength () + «»);
connection.addRequestProperty(requestEntity.getContentType().getName (), requestEntity.getContentType().getValue ());
requestEntity.writeTo(connection.getOutputStream ());
String responseBody = IOUtils.toString(connection.getInputStream ());
if(connection.getResponseCode () <200 || connection.getResponseCode ()> = 400) {
throw new IOException («HTTP Status " + connection.getResponseCode () + ": " + connection.getHeaderFields () + "n» + responseBody);
}
BlobKey blobKey = new BlobKey (responseBody);
ImagesService imagesService = ImagesServiceFactory.getImagesService ();
ServingUrlOptions servingOptions = ServingUrlOptions. Builder. withBlobKey (blobKey);
String servingUrl = imagesService.getServingUrl (servingOptions);
user.setProperty («mBlobKey», responseBody);
user.setProperty («mServingUrl», servingUrl);
profile.setmImage (servingUrl);
} catch (Exception e) {
e.printStackTrace ();
}
datastore. put (user);
return profile;
}
}
import com. google. appengine. api. blobstore. BlobKey;
import com. google. appengine. api. blobstore. BlobstoreService;
import com. google. appengine. api. blobstore. BlobstoreServiceFactory;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class BlobUploadServlet extends HttpServlet {
@Override
public void doPost (HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
BlobstoreService blobService = BlobstoreServiceFactory.getBlobstoreService ();
List <BlobKey> blobs = blobService.getUploads(req).get («file»);
if (blobs == null || blobs.isEmpty ()) throw new IllegalArgumentException («No blobs given»);
BlobKey blobKey = blobs.get (0);
res.setStatus(HttpServletResponse.SC_OK);
res.setContentType («text/plain»);
PrintWriter out = res.getWriter ();
out.print(blobKey.getKeyString ());
out. flush ();
out.close ();
}
}
Fragment + GoogleMap
Файл app_bar_main. xml:
<?xml version=«1.0» encoding=«utf-8»? >
<android.support.design.widget.CoordinatorLayout
xmlns: android="http://schemas.android.com/apk/res/android"
xmlns: app="http://schemas.android.com/apk/res-auto"
xmlns: tools="http://schemas.android.com/tools"
android: id="@+id/bar_main»
android: layout_width=«match_parent»
android: layout_height=«match_parent»
android: fitsSystemWindows=«true»
tools:context="com.tmsoftstudio.meetlove.MainActivity»>
<android.support.design. widget. AppBarLayout
android: layout_width=«match_parent»
android: layout_height=«wrap_content»
android: theme="@style/AppTheme. AppBarOverlay»>
<android.support.v7.widget.Toolbar
android: id="@+id/toolbar»
android: layout_width=«match_parent»
android: layout_height=»? attr/actionBarSize»
android: background=»? attr/colorPrimary»
app: popupTheme="@style/AppTheme. PopupOverlay»
/>
</android.support.design. widget. AppBarLayout>
<RelativeLayout
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»
app: layout_behavior="@string/appbar_scrolling_view_behavior»
android: id="@+id/content»
>
</RelativeLayout>
<android.support.design. widget. FloatingActionButton
android: id="@+id/fab»
android: layout_width=«wrap_content»
android: layout_height=«wrap_content»
android: layout_gravity=«bottom|end»
android: layout_margin="@dimen/fab_margin»
android: src="@android: drawable/ic_input_add»
app: layout_anchor="@id/content»
app: layout_anchorGravity=«top|start»
/>
</android.support.design.widget.CoordinatorLayout>
Файл activity_maps. xml:
RelativeLayout
xmlns: android="http://schemas.android.com/apk/res/android"
android: layout_width=«wrap_content»
android: layout_height=«wrap_content»
android: id="@+id/map_container»>
<fragment
xmlns: map="http://schemas.android.com/apk/res-auto"
android: id="@+id/map»
android:name="com.google.android.gms.maps.SupportMapFragment»
android: layout_width=«wrap_content»
android: layout_height=«wrap_content»
/>
</RelativeLayout>
Код фрагмента:
import android.content.Context;
import android. os. Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps. GoogleMap;
import com.google.android.gms.maps. OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps. UiSettings;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;
public class ViewMapFragment extends Fragment implements OnMapReadyCallback {
private static final String ARG_PARAM = «param»;
private String mParam;
private GoogleMap mMap;
private SupportMapFragment fragment;
private OnViewMapFragmentInteractionListener mListener;
public ViewMapFragment () {
// Required empty public constructor
}
public static ViewMapFragment newInstance (String param) {
ViewMapFragment fragment = new ViewMapFragment ();
Bundle args = new Bundle ();
args. putString (ARG_PARAM, param);
fragment.setArguments (args);
return fragment;
}
@Override
public void onCreate (Bundle savedInstanceState) {
super. onCreate (savedInstanceState);
if (getArguments ()!= null) {
mParam = getArguments().getString (ARG_PARAM);
}
}
@Override
public View onCreateView (LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view= inflater.inflate(R.layout.activity_maps, container, false);
return view;
}
@Override
public void onActivityCreated (Bundle savedInstanceState) {
super. onActivityCreated (savedInstanceState);
FragmentManager fm = getChildFragmentManager ();
fragment = (SupportMapFragment) fm.findFragmentById(R.id.map);
if (fragment == null) {
fragment = SupportMapFragment.newInstance ();
fm.beginTransaction().replace(R.id.map_container, fragment).commit ();
}
fragment.getMapAsync (this);
}
@Override
public void onMapReady (GoogleMap googleMap) {
mMap = googleMap;
UiSettings settings = mMap.getUiSettings ();
settings.setZoomControlsEnabled (true);
LatLng sydney = new LatLng (-34, 151);
mMap.addMarker (new MarkerOptions ().position (sydney).title («Marker in Sydney»));
mMap.animateCamera(CameraUpdateFactory.newLatLngZoom (sydney, 14.0f));
}
public void onViewMapPressed () {
if (mListener!= null) {
mListener. onViewMapFragmentInteraction ();
}
}
@Override
public void onAttach (Context context) {
super. onAttach (context);
if (context instanceof OnViewMapFragmentInteractionListener) {
mListener = (OnViewMapFragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString ()
+ " must implement OnFragmentInteractionListener»);
}
}
@Override
public void onDetach () {
super. onDetach ();
mListener = null;
}
public interface OnViewMapFragmentInteractionListener {
void onViewMapFragmentInteraction ();
}
}
Вызов фрагмента:
FragmentManager fragmentManager = getSupportFragmentManager ();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction ();
ViewMapFragment fragmentMap = new ViewMapFragment ();
fragmentTransaction.replace(R.id.content, fragmentMap);
fragmentTransaction.addToBackStack (null);
fragmentTransaction.commit ();
fab. hide ();
Файл манифеста:
<uses-permission android:name="android.permission.INTERNET» />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE» />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION» />
<meta-data
android:name="com.google.android.geo. API_KEY»
android: value="@string/google_maps_key» />
Файл google_maps_api. xml:
<resources>
<string name=«google_maps_key» templateMergeStrategy=«preserve» translatable=«false»>
Aiza…
</string>
</resources>
Правообладателям!
Это произведение, предположительно, находится в статусе 'public domain'. Если это не так и размещение материала нарушает чьи-либо права, то сообщите нам об этом.