初心者のAndroidプログラマヌ向けのヒントずレシピ

こんばんは、芪愛なるhabrayuzery。



この蚘事では、Androidでの開発経隓を共有したいず思いたす。

開発䞭の補品の機胜に察する芁件により、さたざたな技術的タスクが発生したした。その䞭には、些现なこず、さたざたなブログで噛むこず、非垞に曖昧なこず、そしお明癜な゜リュヌションがありたした。 .NET開発者ずしお私がなじみのないものに出䌚いたした。 人生を倧幅に簡玠化するツヌルの存圚に぀いお孊びたした。 すべおの初心者のAndroidプレヌダヌも同様の方法で動䜜するず思いたす。 そのような蚘事の開発、怜玢、実隓に費やす時間の最倧3分の1を節玄できたした。



したがっお、この投皿では、迅速か぀正確にアプリケヌションを䜜成するのに圹立぀レシピずヒントのコレクションに泚目したす。



この蚘事は初心者を察象ずしおいたすが、経隓豊富な開発者は有甚なポむントを芋぀けるこずができたす。 Androidアプリケヌションの構築の基本を読み、すばらしいStartAndroidリ゜ヌスを知っおいお、HelloWorldを実行できるこずを前提ずしおいたす。 同時に、あなたは本栌的なアプリケヌションを䜜成した経隓がなく、この欠点に察凊し始めたばかりです。 私にずっお、これはAndroid向けの最初のプロゞェクトでした。



開始する



私のパヌトナヌず私は、Google Playの興味深い補品を䜜成するこずを長い間考えおいたした。 ある晎れた日、タクシヌ広告で別のSMSを読みながら、SMSスパムを凊理するアプリケヌションを䜜成するずいうアむデアが浮䞊したした。 小芏暡なチヌムに比范的簡単に実装できる実甚的なアプリケヌションがあるため、私たちにずっおは面癜そうに思えたした。



次に、特定の芁件のセットが開発され、解決すべきタスクのセットが圢成されたした。 それらの最も興味深いもの



この蚘事では、次の点は省略されたす。



プロゞェクトの準備



HelloWorld、そしおAndroidアプリケヌション甚のテンプレヌトプロゞェクトを䜜成する方法は既に知っおいたす。 次に、さらに必芁なものを芋おみたしょう。 これらのツヌルの存圚を知っおいれば、むンタヌネット䞊でそれらの䜿甚方法を簡単に芋぀けるこずができたす。 結局のずころ、䞻な問題は、ツヌルの䜿甚方法を理解するこずではなく、ツヌルが存圚するこずを芋぀けるこずです。



1プラットフォヌムに䟝存しないActionBar画面䞊郚のメニュヌを実装するには、 ActionBarSherlockが必芁です。 公匏サむトからダりンロヌドし、゜ヌスコヌドの圢匏でWorkspaceにむンポヌトしたす。 ラむブラリによるいく぀かのリ゜ヌスの過負荷には既知の問題があるため、ラむブラリjarファむルだけでは十分ではありたせん。



2SDK sdk \ extras \ google \ google_play_services \ libproject \ google-play-services_lib \から゜ヌスのGoogle Playサヌビスの圢匏でワヌクスペヌスにむンポヌトしたす。 これは、請求ず承認に必芁になりたす。



3プロゞェクトラむブラリのlibフォルダヌに入れたすその前に、むンタヌネットでそれらを芋぀けたす。

* acra.jar-アプリケヌションクラッシュレポヌトメカニズムACRAを実装したす。

* android-support-v4.jar-叀いバヌゞョンのAndroidずの互換性を実装したす。

* roboguice-2.0.jar、roboguice-sherlock-1.5.jar-䟝存性泚入の実装甚。roboguiceでの実装が必芁な堎合。

* ormlite-core.jar、ormlite-android.jar- sqlite Androidベヌス甚の人気のある「軜量」 ORM 。

* joda-time.jar-日付を操䜜するためのラむブラリ。

* jdom.jar、gson.jar-JSONを操䜜するため。

* checkout.jar-課金甚このラむブラリを遞択したした。APIを盎接操䜜するよりもCheckoutの方が䟿利です。



CMCの取埗ず解析



以䞋では、Androidで4.4KitKatよりも䜎いバヌゞョンで実行されるメ゜ッドを瀺したす。これは、GoogleがこのバヌゞョンでCMC凊理のアプロヌチを根本的に倉曎したためです。 KitKatを䜿甚した䜜業の説明は、アプリケヌションで実装されるずきに远加したす。



CMCを䜿甚するには、マニフェストで蚱可が必芁です。



<uses-permission android:name="android.permission.RECEIVE_SMS" /> <uses-permission android:name="android.permission.WRITE_SMS" /> <uses-permission android:name="android.permission.READ_SMS" />
      
      





どこで

RECEIVE_SMS-CMCを受信するアプリケヌションぞの蚱可。

READ_SMS-携垯電話のメモリからSMSを読み取る蚱可。 必芁ないず思われたすが、この蚱可がないず蚘録は機胜したせん。

WRITE_SMS-電話のメモリにCMCを曞き蟌む蚱可。



SmsBroadcastReceiverが受け入れたCMCむベントリスナヌを䜜成したしょう。 電話機がSMSを受信し、SMSを凊理するためのメむンプロセスの実行を開始するず呌び出されたす。



SmsBroadcastReceiver
 //BroadcastReceiver      public class SmsBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // -    ,    Bundle bundle = intent.getExtras(); //    pdus -      Object[] pdus = (Object[]) bundle.get("pdus"); if (pdus.length == 0) { return; //      } //  CMC Sms sms = SmsFromPdus(pdus, context); // ,   Boolean isClearFromSpam = SuperMegaMethodForResolving Spam(sms, context); if (!isClearFromSpam) { //    -   CMC  abortBroadcast(); return; } } private Sms SmsFromPdus(Object[] pdus, Context context) { Sms sms = new Sms(); for (int i = 0; i < pdus.length; i++) { SmsMessage smsMessage = SmsMessage.createFromPdu((byte[]) pdus[i]); sms.Text += smsMessage.getMessageBody(); //   (CMC   "") } SmsMessage first = SmsMessage.createFromPdu((byte[]) pdus[0]);//    sms.SenderId = first.getOriginatingAddress(); // Date receiveDate = new Date(first.getTimestampMillis()); // sms.RecieveDate = receiveDate; sms.Status = first.getStatus(); // (, , ) return sms; } } public class Sms{ public String SenderId; public String Text; public Date RecieveDate; public int Status; }
      
      







onReceiveが10秒未満で完了するこずが非垞に重芁です。 メ゜ッドがより長い期間制埡を行う堎合、実行は䞭断され、むベントは他のハンドラヌに優先順䜍でディスパッチされたす。

私の堎合、SuperMegaMethodForResolvingは、連絡先リストずロヌカル送信者リストにSMSが存圚するかどうかを確認したす。これには1秒もかかりたせん。 次に、専甚ストリヌムに制埡が䞎えられ、onReceiveがabortBroadcastを呌び出したす。これにより、他のハンドラヌがSMS基本SMSアプリケヌションを含むを受信できなくなりたす。



次に、CMC受信むベントのSmsBroadcastReceiverに眲名する必芁がありたす。 これを行うには、マニフェストのアプリケヌションブロックで、SmsBroadcastReceiverずいう名前のandroid.provider.Telephony.SMS_RECEIVEDむベントリスナヌを宣蚀したす。これは、SMS_RECEIVEDシステムむベントをリッスンし、優先床は2147483631です。優先床は最倧2 ^ 31です。 同時に、Googleは999より倧きい倀を䜿甚するこずを掚奚しおいたせん。しかし、倚くのアプリケヌションはそれらを䜿甚したす。 このアプリケヌションは、私が知っおいる最高の優先順䜍を求めたす。



 <receiver android:name="su.Jalapeno.AntiSpam.SystemService.SmsBroadcastReceiver" android:enabled="true" android:exported="true" > <intent-filter android:priority="2147483631" > <action android:name="android.provider.Telephony.SMS_RECEIVED" /> </intent-filter> </receiver>
      
      





起動電話での自動起動アプリケヌション



デバむスの電源をオンにした埌、アプリケヌションを起動する必芁がある堎合がありたす。たずえば、通知を通じお未確認の䞍審なSMSの存圚をナヌザヌに通知する堎合です。



起動するには、マニフェストの蚱可が必芁です。



 <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
      
      





どこで

RECEIVE_BOOT_COMPLETED-ダりンロヌドむベントをリッスンする暩限



「ダりンロヌド」むベントServiceBroadcastReceiverのリスナヌを䜜成したす。



 public class ServiceBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // - } }
      
      





その䞭で、電話をオンにした埌、必芁なアクションを実行したす。



次に、ServiceBroadcastReceiverを電話ブヌトむベントに眲名する必芁がありたす。 これを行うには、マニフェストのアプリケヌションブロックで、SmsBroadcastReceiverずいう名前のandroid.intent.action.BOOT_COMPLETEDむベントリスナヌを宣蚀したす。



 <receiver android:name="su.Jalapeno.AntiSpam.SystemService.ServiceBroadcastReceiver" android:exported="true" > <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver>
      
      





Webリク゚スト



したがっお、CMCがあり、サヌバヌに接続する必芁がありたす。 次のようにマニフェストを補完したす。



 <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
      
      





どこで

むンタヌネット-Web芁求を送信する蚱可。

ACCESS_NETWORK_STATE-ネットワヌクステヌタス接続されおいるかどうか、3gたたはwifiを読み取る蚱可。



リク゚ストの䜜成は簡単です。それらの堎合、基本的なAndroid httpクラむアントorg.apache.http.client.HttpClientを䜿甚したす。



ロヌカルデヌタベヌスのデヌタストレヌゞ



ここで説明するようにすべおをしたす 。



蚭定保存



AndroidはSharedPreferencesメカニズムを提䟛するため、app.config、* .ini、たたはアプリケヌションデヌタベヌスを䜿甚しおアプリケヌション蚭定を保存するこずはありたせん。



Googleトヌクン認蚌



Googleのドキュメントなど、䜓系的で䞍完党な情報が䞍足しおいるため、これに最も時間がかかりたした。 したがっお、私たちのタスクは、アプリケヌションに関するナヌザヌ情報ず秘密情報を含むアプリケヌションを介しおGoogleから眲名付きトヌクンを取埗するこずです。 これにより、トヌクンが攻撃者によっお生成されたものではないず考える理由が埗られたす。 この問題を解決するには、 CrossClientAuthメカニズムを䜿甚したす。



以䞋を実行する必芁がありたす。

1アプリケヌション蚌明曞を取埗し、それを䜿甚しおアプリケヌションに眲名したす。 これは、りィザヌドを䜿甚しおEclipseに簡単に実装できたす。 パッケヌゞ゚クスプロヌラヌ-> Androidツヌル->眲名枈みアプリケヌションパッケヌゞの゚クスポヌトでプロゞェクトを右クリックしたす。 りィザヌドは、新しい蚌明曞ストアの䜜成、蚌明曞の生成、指定したパスワヌドで保護されたストアぞの配眮を提案したす。 蚌明曞のハッシュは将来必芁になるため、忘れずに保存しおください。



2 Googleコン゜ヌルでプロゞェクトを䜜成したす。 次に、䜜成したプロゞェクトを開き、巊偎のパネルの[Apiず認蚌]-> [資栌情報]タブに移動したす。 ここでは、サヌバヌ偎ずAndroidクラむアント甚のクラむアントIDのペアを䜜成する必芁がありたす。 [新しいクラむアントIDを䜜成]をクリックしたす。AndroidアプリケヌションのクラむアントIDが必芁です。







スクリヌンショットに瀺されおいるように、正しいパッケヌゞ名ず蚌明曞の指王を蚘入しおください。 䜜成が完了するず、生成された情報ずクラむアントIDが蚘茉されたプレヌトが取埗されたす。 サヌバヌ䞊で必芁です。



次に、Webアプリケヌションタむプの新しいクラむアントIDを䜜成したす。 私の堎合、httpを介したGoogle Webリ゜ヌスずのやり取りがないため、アドレスは任意に指定できたす。 その結果、新しいクラむアントIDを取埗したす。これはクラむアントで既に必芁です。



3 GoogleAuthUtilなど、クラむアントコヌド党䜓をむンタヌネット䞊で芋぀けるこずができたす。 重芁なポむントのみに泚目したす。Scopeを正しく構成する方法、およびIDを取埗する堎所



コヌド
 //  @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == REQUEST_CODE_PICK_ACCOUNT) { if (resultCode == RESULT_OK) { Email = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME); getUsername(); } } super.onActivityResult(requestCode, resultCode, data); } private void pickUserAccount() { String[] accountTypes = new String[] { "com.google" }; Intent intent = AccountPicker.newChooseAccountIntent(null, null, accountTypes, false, null, null, null, null); startActivityForResult(intent, REQUEST_CODE_PICK_ACCOUNT); } //   (   Try catch     //WEB_CLIENT_ID  Client ID for web application final private String WEB_CLIENT_ID = "1999999-aaaaaaaaaaaaaaaaaaaaaaaaa.apps.googleusercontent.com"; //""  Client id.       String SCOPE = String.format("audience:server:client_id:%s", WEB_CLIENT_ID); //   String token = GoogleAuthUtil.getToken(_activity, Email, SCOPE);
      
      







トヌクンをサヌバヌに転送するために残りたす



4トヌクンを怜蚌するサヌバヌコヌド

Microsoft.IdentityModel.Tokens.JWT Nugetを䜿甚したす。 以䞋のコヌドを䜿甚するず、ナヌザヌのGoogleIdずそのメヌルを取埗できたす。



コヌド
 public string GetUserIdByJwt(string jwt, out string userEmail) { userEmail = string.Empty; string userId = null; // Client ID   (Client ID for web application) string audience = "111111111111111111-aaaaaaaaaaaaaaaaaaaaa.apps.googleusercontent.com"; // Client ID  (Client ID for Android application) string azp = "1111111111111-aaaaaaaaaaaaaaaaaaaaaaaa.apps.googleusercontent.com"; var tokenHandler = new JWTSecurityTokenHandler(); SecurityToken securityToken = tokenHandler.ReadToken(jwt); var jwtSecurityToken = securityToken as JWTSecurityToken; userEmail = GetClaimValue(jwtSecurityToken, "email"); userId = GetClaimValue(jwtSecurityToken, "id"); var validationParameters = new TokenValidationParameters() { AllowedAudience = audience, ValidIssuer = "accounts.google.com", ValidateExpiration = true, //     . //         Google //   Microsoft.IdentityModel ValidateSignature = false, }; try { // Exception,     ClaimsPrincipal claimsPrincipal = tokenHandler.ValidateToken(jwtSecurityToken, validationParameters); //,   Client Id    bool allGood = ValidateClaim(jwtSecurityToken, "azp", azp) && ValidateClaim(jwtSecurityToken, "aud", audience); if (!allGood) { userId = null; } } catch { userId = null; } return userId; } //   Claim   private static bool ValidateClaim(JWTSecurityToken securityToken, string type, string value) { string claim = GetClaimValue(securityToken, type); if (claim == null) return false; return claim == value; } //   Claim (  KeyValuePair) private static string GetClaimValue(JWTSecurityToken securityToken, string type) { var claim = securityToken.Claims.SingleOrDefault(x => x.Type == type); if (claim == null) return null; return claim.Value; }
      
      







賌入のメカニズムを䜿甚する



開始するには、[コンテンツの販売]タブのアプリケヌションプロゞェクトのGoogle開発コン゜ヌルで必芁な補品を䜜成する必芁がありたす。 クラむアントは、 チェックアりトの䟋に基づいお蚘述したす。 Checkoutラむブラリをより完党に理解するための、請求に関する私のコヌドの抜粋を以䞋に瀺したす。



Applicationクラスの倉曎
 public class MyApplication extends Application { private static final Products products = Products.create().add(IN_APP, asList("     ", "  2   ")); private final Billing billing = new Billing(this, new Billing.Configuration() { @Nonnull @Override public String getPublicKey() { String base64EncodedPublicKey = "    ,         API  "; return base64EncodedPublicKey; } @Nullable @Override public Cache getCache() { return Billing.newCache(); } }); @Nonnull private final Checkout checkout = Checkout.forApplication(billing, products); @Nonnull private static MyApplication instance; public MyApplication() { instance = this; } @Override public void onCreate() { super.onCreate(); billing.connect(); } @Nonnull public static MyApplication get() { return instance; } @Nonnull public Checkout getCheckout() { return checkout; } }
      
      







賌入したアクティビティ
 public class BillingActivity extends RoboSherlockActivity { private Sku _skuAccess; @Nonnull protected final ActivityCheckout checkout = Checkout.forActivity(this, MyApplication.get().getCheckout()); @Nonnull protected Inventory inventory; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); _skuAccess = null; _activity = this; checkout.start(); checkout.createPurchaseFlow(new PurchaseListener()); inventory = checkout.loadInventory(); inventory.whenLoaded(new InventoryLoadedListener()); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); checkout.onActivityResult(requestCode, resultCode, data); } @Override protected void onDestroy() { checkout.stop(); checkout.destroyPurchaseFlow(); super.onDestroy(); } @Nonnull public ActivityCheckout getCheckout() { return checkout; } public void Buy(View view) { purchase(_skuAccess); } private void purchase(@Nonnull final Sku sku) { boolean billingSupported = checkout.isBillingSupported(IN_APP); if (!billingSupported) { return; } checkout.whenReady(new Checkout.ListenerAdapter() { @Override public void onReady(@Nonnull BillingRequests requests) { requests.purchase(sku, null, checkout.getPurchaseFlow()); } }); } private class PurchaseListener extends BaseRequestListener<Purchase> { @Override public void onSuccess(@Nonnull Purchase purchase) { onPurchased(); } private void onPurchased() { //  -            inventory.load().whenLoaded(new InventoryLoadedListener()); } @Override public void onError(int response, @Nonnull Exception ex) { // it is possible that our data is not synchronized with data on // Google Play => need to handle some errors if (response == ResponseCodes.ITEM_ALREADY_OWNED) { onPurchased(); } else { super.onError(response, ex); } } } private class InventoryLoadedListener implements Inventory.Listener { private String _purchaseOrderId; @Override public void onLoaded(@Nonnull Inventory.Products products) { final Inventory.Product product = products.get(IN_APP); if (product.isSupported()) { boolean isPurchased = InspectPurchases(product); // - } } private boolean InspectPurchases(Product product) { List<Sku> skus = product.getSkus(); Sku sku = skus.get(0); //   final Purchase purchase = product.getPurchaseInState(sku, Purchase.State.PURCHASED); boolean isPurchased = purchase != null && !TextUtils.isEmpty(purchase.token); if (isPurchased) { //  ? return true; } else { //  -     _skuAccess = sku; return false; } } } private abstract class BaseRequestListener<Req> implements RequestListener<Req> { @Override public void onError(int response, @Nonnull Exception ex) { } } }
      
      







おわりに



今のずころすべおです。 あらゆる偎面をより詳现に分析したい堎合-蚘事を曞いお、補足したす。 この投皿が初心者のAndroidプログラマヌが小さな熊手を砎り、fireを螏たないようにしお時間を節玄するのに圹立぀こずを願っおいたす。 ここで完成したアプリケヌションを詊すこずができたす 。



All Articles