ビットコインの為替レートを表示するAndroidウィジェットの開発

こんにちは。 背景:最近、ビットコイン(BTC)暗号通貨レートの成長を追跡しています。 以前は、取引所のウェブサイトで見積もりを表示していましたが、最新情報を表示するスマートフォンの作業画面に小さなウィジェットを置く方がはるかに便利です。



市場には、暗号通貨レートを示すさまざまなウィジェットがたくさんあります。 しかし、独自の何かを作成する方がはるかに興味深いです。 この記事では、Android OSを実行するガジェット用のウィジェットを作成した経験を簡単に説明します。



この記事の内容を理解するには、Android Studio IDEでのJavaおよびAndroid開発の最初の知識が必要です。 そうでない場合は、ここにアクセスしてください: Android開発者



私の小さなウィジェットは、ビットコインの価格をドルで表示し、この価格が関連する時間を表示します。 このプラットフォームはurl btc-e.nz/api/3/ticker/btc_usdでのgetリクエストに応じて便利なJSON形式でコースを提供するため、情報はロシア語圏の取引所BTC-eからダウンロードされます。



交換応答の例:



{"btc_usd":{"high":880,"low":836,"avg":858,"vol":3774491.17766,"vol_cur":4368.01172,"last":871.999,"buy":872,"sell":870.701,"updated":1482754417}}
      
      





したがって、開発を開始するには、適切なテンプレートを使用してIDEで新しいプロジェクトを作成します。 [新しいAndroid Studioプロジェクトの開始]を選択し、プロジェクトの名前と場所を入力し、ターゲットデバイスとAPIのバージョンを選択して、[アクティビティを追加しない]項目が必要です。



スクリーンショット
画像

画像

画像



IDEワークスペースが開いたら、組み込みテンプレートを使用して空のウィジェットを作成します。 これを行うには、プロジェクトファイルツリーで、アプリフォルダーのコンテキストメニューを呼び出し、[新規]→[ウィジェット]→[アプリウィジェット]を選択します。



スクリーンショット
画像



いくつかのファイルが作成されますが、そのうち3つが特に重要です。



1つ目は、主要なウィジェットパラメータを含むxmlファイル(res→xml→btcwidget_info.xml)です。



 <source lang="xml"><?xml version="1.0" encoding="utf-8"?> <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" android:initialKeyguardLayout="@layout/btcwidget" android:initialLayout="@layout/btcwidget" android:minHeight="40dp" android:minWidth="110dp" android:previewImage="@drawable/example_appwidget_preview" android:resizeMode="horizontal|vertical" android:updatePeriodMillis="180000" android:widgetCategory="home_screen"></appwidget-provider>
      
      





initialLayoutパラメーターは、ウィジェットの視覚的なマークアップを使用してxmlファイルの名前を指定します。 minHeightとmin-Widthは追加されるウィジェットの最小サイズです。updatePeriodMillisは情報がms単位で更新される時間ですが、30分ごとに1回を超えないようにします(10 msパラメーターは最低でも30分と認識されます)。



2番目のxmlファイル(res→layout→btcwidget.xml)には、ウィジェットの視覚表示(視覚要素のマークアップ)のパラメーターが含まれています。



RelativeLayout( Layouts )マークアップ内に1つの視覚的なTextView要素の説明が含まれています。



 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#09C" android:padding="@dimen/widget_margin"> <TextView android:id="@+id/appwidget_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_centerVertical="true" android:layout_margin="8dp" android:background="#09C" android:contentDescription="@string/appwidget_text" android:text="@string/appwidget_text" android:textColor="#ffffff" android:textSize="20sp" android:textStyle="bold|italic" /> </RelativeLayout>
      
      





最も興味深いのは、Java言語のウィジェットソースコードテンプレートを含む3番目のファイルです。 このフォームでは、テンプレートには機能的な負荷はありませんが、コードのコメントから、どのメソッドがいつどの責任で実行されるかを理解できます:



ウィジェットコードテンプレート
 /** * Implementation of App Widget functionality. */ public class BTCWidget extends AppWidgetProvider { static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) { CharSequence widgetText = context.getString(R.string.appwidget_text); // Construct the RemoteViews object RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.btcwidget); views.setTextViewText(R.id.appwidget_text, widgetText); // Instruct the widget manager to update the widget appWidgetManager.updateAppWidget(appWidgetId, views); } @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { // There may be multiple widgets active, so update all of them for (int appWidgetId : appWidgetIds) { updateAppWidget(context, appWidgetManager, appWidgetId); } } @Override public void onEnabled(Context context) { // Enter relevant functionality for when the first widget is created } @Override public void onDisabled(Context context) { // Enter relevant functionality for when the last widget is disabled } }
      
      







Android OSを使用すると、ウィジェットの多数のインスタンスを作成でき、その動作は上記のコードで完全に記述されることをすぐに言う価値があります。 ウィジェットの動作とライフサイクルをより深く理解するには、 Androidウィジェットの記事を読むことをお勧めします。 以下のコードコメントでは、いくつかのポイントのみを説明します。



次の困難に直面しました。システムは、バッテリーを節約するために、30分ごとに1回しかウィジェットを更新できないようにします(updateAppWidgetメソッドを使用)。 しかし、適切なタイミングでデータを更新できるようにしたいと考え、この制限を回避する方法を見つけました。 これを行うには、ウィジェットをクリックして更新を強制するようにウィジェットをプログラムしました。 このアクションを次のように実装しました。ウィジェットをクリックすると、システムにインテントが送信され、ウィジェット自体によってキャッチされ、データ更新の起動によって処理されます。 誰かが簡単な方法を知っている場合-私はコメントでアドバイスを喜んでいるでしょう。



ウィジェットのソースコードを追加
 /** * Implementation of App Widget functionality. */ public class BTCwidget extends AppWidgetProvider { private static final String SYNC_CLICKED = "btcwidget_update_action"; private static final String WAITING_MESSAGE = "Wait for BTC price"; public static final int httpsDelayMs = 300; //  ,     static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) { // RemoteViews        : RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.btcwidget); //   -  TextView views.setTextViewText(R.id.appwidget_text, WAITING_MESSAGE); appWidgetManager.updateAppWidget(appWidgetId, views); String output; //         //      -   HTTPRequestThread thread = new HTTPRequestThread(); thread.start(); try { while (true) { Thread.sleep(300); if(!thread.isAlive()) { output = thread.getInfoString(); break; } } } catch (Exception e) { output = e.toString(); } //    views.setTextViewText(R.id.appwidget_text, output); // Instruct the widget manager to update the widget appWidgetManager.updateAppWidget(appWidgetId, views); } @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { RemoteViews remoteViews; ComponentName watchWidget; remoteViews = new RemoteViews(context.getPackageName(), R.layout.btcwidget); watchWidget = new ComponentName(context, BTCwidget.class); //         ,    remoteViews.setOnClickPendingIntent(R.id.appwidget_text, getPendingSelfIntent(context, SYNC_CLICKED)); appWidgetManager.updateAppWidget(watchWidget, remoteViews); //    for (int appWidgetId : appWidgetIds) { updateAppWidget(context, appWidgetManager, appWidgetId); } } //   ,         //   @Override public void onReceive(Context context, Intent intent) { super.onReceive(context, intent); if (SYNC_CLICKED.equals(intent.getAction())) { AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); RemoteViews remoteViews; ComponentName watchWidget; remoteViews = new RemoteViews(context.getPackageName(), R.layout.btcwidget); watchWidget = new ComponentName(context, BTCwidget.class); remoteViews.setTextViewText(R.id.appwidget_text, WAITING_MESSAGE); //updating widget appWidgetManager.updateAppWidget(watchWidget, remoteViews); String output; HTTPRequestThread thread = new HTTPRequestThread(); thread.start(); try { while (true) { Thread.sleep(httpsDelayMs); if(!thread.isAlive()) { output = thread.getInfoString(); break; } } } catch (Exception e) { output = e.toString(); } remoteViews.setTextViewText(R.id.appwidget_text, output); //widget manager to update the widget appWidgetManager.updateAppWidget(watchWidget, remoteViews); } } //  protected PendingIntent getPendingSelfIntent(Context context, String action) { Intent intent = new Intent(context, getClass()); intent.setAction(action); return PendingIntent.getBroadcast(context, 0, intent, 0); } }
      
      







HTTPRequestThread.javaクラスの内容:



表示:
 package com.hakey.btcwidget; import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import java.util.Calendar; class HTTPRequestThread extends Thread{ private static final String urlString = "https://btc-e.nz/api/3/ticker/btc_usd"; String getInfoString() { return output; } private String output = ""; private void requestPrice() { try { URL url = new URL(urlString); HttpURLConnection con = (HttpURLConnection) url.openConnection(); con.setRequestMethod("GET"); BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream())); String inputLine; StringBuilder response = new StringBuilder(); while ((inputLine = in.readLine()) != null) { response.append(inputLine); } in.close(); output = "Price: " + JSONParser.getPrice(response.toString()) + "\n" + getTimeStamp(); } catch (Exception e) { output = e.toString(); } } @Override public void run() { requestPrice(); } private String getTimeStamp() { Calendar calendar = Calendar.getInstance(); if(calendar.get(Calendar.MINUTE)>9) { return "Time: " + calendar.get(Calendar.HOUR_OF_DAY) + ":" + calendar.get(Calendar.MINUTE); } else { return "Time: " + calendar.get(Calendar.HOUR_OF_DAY) + ":0" + calendar.get(Calendar.MINUTE); } } }
      
      







サーバー応答パーサー-JSONParser.java:



見る:
 package com.hakey.btcwidget; import org.json.JSONException; import org.json.JSONObject; class JSONParser { static String getPrice(String s) throws JSONException { String price; JSONObject obj = new JSONObject(s); JSONObject pairObj = obj.getJSONObject("btc_usd"); price = pairObj.getString("last"); return price; } }
      
      







上記のウィジェットは次のとおりです。



スクリーンショット
画像



完全なソースコードはこちらから入手できます: github.com/hakeydotom/BTCPriceWidget



All Articles