Android
 Computer >> コンピューター >  >> トラブルシューティング >> Android

AndroidアプリにAPIフェッチャーを組み込む方法を徹底解説

アプリ開発者なら、ユーザーに有益な情報を届けるために、Web APIからデータを取得する機能をアプリに組み込みたい場面があるでしょう。実はそれほど難しい作業ではなく、XML形式でもJSON形式でも実装可能です。このチュートリアルでは、AndroidアプリにシンプルなWeb APIデータフェッチャーを組み込む方法を、ステップごとにわかりやすく解説します。

なお、本チュートリアルでは扱いやすさの観点から、XMLではなくJSONを使用します。

1. レイアウトXMLファイルを作成する

まずはリスト表示用のレイアウトXMLファイルを新規作成し、以下のコードを記述します。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent">
<ListView android:id="@+id/list_title_list" android:layout_width="match_parent" android:layout_height="match_parent"> </ListView>
</LinearLayout>

次に、リストの各行に表示する要素(ImageViewやTextViewなど)を定義するためのXMLファイルを作成します。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="80dp"
android:orientation="vertical"
android:padding="5dp" >
<ImageView
android:id="@+id/iv_icon_social"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_centerVertical="true"
android:visibility="gone" />
<LinearLayout
android:id="@+id/thumbnail"
android:layout_width="fill_parent"
android:layout_height="85dp"
android:layout_marginRight="50dp"
android:layout_marginTop="0dp"
android:layout_toRightOf="@+id/iv_icon_social"
android:gravity="center_vertical"
android:orientation="vertical"
android:padding="5dip"
android:visibility="visible" >

<TextView
android:id="@+id/txt_ttlsm_row"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="10dp"
android:text="Sample text"
android:textSize="18dp"
android:textStyle="bold" />

<TextView
android:id="@+id/txt_ttlcontact_row2"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="0dp"
android:layout_marginTop="3dp"
android:paddingLeft="10dp"
android:maxEms="20"
android:maxLines="2"
android:singleLine="false"
android:ellipsize="end"
android:text="Sample text2"
android:textColor="#808080"
android:textSize="15dp"
android:textStyle="normal" />

</LinearLayout>

</RelativeLayout>

2. AsyncTaskでAPIからデータを取得する

APIからデータを取得するには、メインアクティビティに処理を記述していきます。ここで重要になるのがAsyncTaskです。ネットワーク通信はメインスレッド(UIスレッド)では実行できず、メインスレッドでネットワーク処理を行うとアプリがクラッシュしてしまいます。AsyncTaskは、バックグラウンドで処理を実行しながら、その結果をUIに反映できる便利な仕組みです。概念がわかりにくい場合は、RapidAPIなどで公開されている詳細なAPIチュートリアルを参考にするとよいでしょう。

※補足:AsyncTaskおよびApache HttpClient(DefaultHttpClient)は現在非推奨となっています。最新のAndroid開発では、KotlinコルーチンやOkHttp、Retrofit、Volleyなどの利用が推奨されますが、本記事では元のコードの流れを理解しやすいよう、従来の手法に沿って解説を進めます。

class DownloadFilesTask extends AsyncTask < Void, Void, String >
{
private final ProgressDialog dialog = new ProgressDialog(MainActivity.this);
@Override
protected void onPreExecute()
{
super.onPreExecute();
this.dialog.setMessage("Signing in...");
this.dialog.show();
}
@Override
protected String doInBackground(Void...params)
{
ServiceHandler sh = new ServiceHandler();
String jsonStr = sh.makeServiceCall("https://jsonplaceholder.typicode.com/albums/", ServiceHandler.GET);
Log.d("res1", jsonStr);
return jsonStr;

}
@Override
protected void onPostExecute(String response)
{
super.onPostExecute(response);
Log.d("res2", response);
dialog.dismiss();
if (response != null)
{
try
{

JSONArray arr = new JSONArray(response);
DataModel mDatModel = new DataModel();
for (int i = 0; i < arr.length(); i++)
{

JSONObject c = arr.getJSONObject(i);
String id=c.getString(ID);
String title = c.getString(TITLE);
String uid = c.getString(USER_ID);
id_array.add(id);

// Toast.makeText(getApplicationContext(), title, Toast.LENGTH_LONG).show();
}

adapter = new AAdapter(MainActivity.this, id_array);
l.setAdapter(adapter);
}

catch (Exception e)
{}
}
}
}

MainActivity.java

public class MainActivity extends AppCompatActivity implements AdapterView.OnItemClickListener

{

AAdapter adapter;

ArrayList < String > id_array = new ArrayList < String > ();

ArrayList < String > notice_array = new ArrayList < String > ();

Button b1;

ListView l;

private final static String SERVICE_URI = "https://jsonplaceholder.typicode.com/albums/";

private static final String TAG_QUESTIONS = "Questions";

private static final String USER_ID = "userId";

private static final String ID = "id";

private static final String TITLE = "title";

JSONArray questions = null;

protected void onSaveInstanceState(Bundle outState)

{

super.onSaveInstanceState(outState);

}

protected void onRestoreInstanceState(Bundle savedInstanceState)

{

super.onRestoreInstanceState(savedInstanceState);

}

@Override

protected void onCreate(Bundle savedInstanceState)

{

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

l = (ListView) findViewById(R.id.list);

new DownloadFilesTask().execute();

l.setOnItemClickListener(new AdapterView.OnItemClickListener()

{

@Override

public void onItemClick(AdapterView < ? > parent, View view, int position, long id)

{

String abc = id_array.get(position);

Toast.makeText(getBaseContext(), id_array.get(position), Toast.LENGTH_LONG).show();

Intent n = new Intent(MainActivity.this, Titleshow.class);

n.putExtra("id", id_array.get(position));

startActivity(n);

}

});

}

@Override

public void onItemClick(AdapterView < ? > parent, View view, int position, long id)

{}

APIからのレスポンスは、メインアクティビティ内で受け取ることができます。

3. ServiceHandlerクラスでHTTP通信を行う

続いて紹介するのは、Web APIへリクエストを送信し、レスポンスを受け取るためのServiceHandlerクラスです。GET・POSTといったHTTPメソッドの切り替えも、このクラス内で行います。

ServiceHandler sh = new ServiceHandler();

String jsonStr = sh.makeServiceCall("https://jsonplaceholder.typicode.com/albums/", ServiceHandler.GET);

これはWeb APIへリクエストを送り、レスポンスを取得するためのサービスハンドラークラスです。

ServiceHandler.java

class ServiceHandler

{

static String response = null;

public final static int GET = 1;

public final static int POST = 2;

public ServiceHandler()

{}

/**

* サービスコールの実行

*

* @url - リクエスト先のURL

* @method - HTTPリクエストメソッド

*/

public String makeServiceCall(String url, int method)

{

return this.makeServiceCall(url, method, null);

}

/**

* サービスコールの実行

*

* @url - リクエスト先のURL

* @method - HTTPリクエストメソッド

* @params - HTTPリクエストパラメータ

*/

public String makeServiceCall(String url, int method, List < NameValuePair > params)

{

try

{

// HTTPクライアント

DefaultHttpClient httpClient = new DefaultHttpClient();

HttpEntity httpEntity = null;

HttpResponse httpResponse = null;

// HTTPリクエストメソッドの種類を判定

if (method == POST)

{

HttpPost httpPost = new HttpPost(url);

// POSTパラメータの追加

if (params != null)

{

httpPost.setEntity(new UrlEncodedFormEntity(params));

}

httpResponse = httpClient.execute(httpPost);

}

else if (method == GET)

{

// URLへのパラメータ追加

if (params != null)

{

String paramString = URLEncodedUtils.format(params, "utf-8");

url += "?" + paramString;

}

HttpGet httpGet = new HttpGet(url);

httpResponse = httpClient.execute(httpGet);

}

httpEntity = httpResponse.getEntity();

response = EntityUtils.toString(httpEntity);

}

catch (UnsupportedEncodingException e)

{

e.printStackTrace();

}

catch (ClientProtocolException e)

{

e.printStackTrace();

}

catch (IOException e)

{

e.printStackTrace();

}

return response;

}

}

4. カスタムアダプターを作成する

AsyncTaskの中でアダプターをセットする処理が登場しますが、そのためには独自のカスタムアダプターを作成する必要があります。

public class AAdapter extends BaseAdapter

{

private Activity activity;

// private ArrayList<HashMap<String, String>> data;

private static ArrayList title;

private static LayoutInflater inflater = null;

public AAdapter(Activity a, ArrayList b)

{

activity = a;

this.title = b;

inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

}

public int getCount()

{

return title.size();

}

public Object getItem(int position)

{

return position;

}

public long getItemId(int position)

{

return position;

}

public View getView(int position, View convertView, ViewGroup parent)

{

View vi = convertView;

if (convertView == null) vi = inflater.inflate(R.layout.abcd, null);

TextView title2 = (TextView) vi.findViewById(R.id.txt_ttlsm_row); // タイトル

String song = title.get(position).toString();

title2.setText(song);

TextView title22 = (TextView) vi.findViewById(R.id.txt_ttlcontact_row2); // お知らせ

String song2 = title.get(position).toString();

title22.setText(song2);

return vi;

}

}

アダプタークラスはAndroidにおいて抽象クラスとして扱われるため、すべてのメソッドを実装しなければなりません。中でも特に重要なのがgetView()メソッドです。ここにコードを追加することで、2つ目のXMLファイル(行レイアウト)を画面に表示できるようになります。

inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

5. 画像読み込み用アダプターとViewHolderパターン

APIフェッチャーがインターネット上の画像を読み込めるようにするには、画像ローダー用のアダプタークラスも用意しましょう。

public class MyAdapter extends BaseAdapter

{

ImageLoader imageLoader;

private Context ctx;

ArrayList < AlbumData > list;

public MyAdapter(Context ctx, ArrayList < AlbumData > mDataList)

{

list = mDataList;

this.ctx = ctx;

}

public int getCount()

{

return list.size();

}

public Object getItem(int position)

{

return position;

}

public long getItemId(int position)

{

return position;

}

public View getView(int position, View convertView, ViewGroup parent)

{

// View vi = convertView;

ViewHolder viewHolder;

if (convertView == null)

{

LayoutInflater vi = (LayoutInflater) ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

convertView = vi.inflate(R.layout.abcde, null);

viewHolder = new ViewHolder();

viewHolder.imageView = (ImageView) convertView.findViewById(R.id.image);

viewHolder.textView = (TextView) convertView.findViewById(R.id.txt_ttlsm_row);

convertView.setTag(viewHolder);

}

else

{

viewHolder = (ViewHolder) convertView.getTag();

}

AlbumData data = list.get(position);

String a = data.getThumbnailUrl();

if (data != null)

{

viewHolder.textView.setText(data.getTitle());

String url = a;

Glide.with(ctx).load(url).centerCrop().placeholder(R.drawable.abhi).crossFade().into(viewHolder.imageView);

/* Picasso.with(ctx)

.load("https://preview.keenthemes.com/conquer/assets/plugins/jcrop/demos/demo_files/image2.jpg")

.into(viewHolder.imageView);*/

/* ImageLoader imageLoader = ImageLoader.getInstance();

imageLoader.displayImage("https://www.keenthemes.com/preview/conquer/assets/plugins/jcrop/demos/demo_files/image1.jpg", viewHolder.imageView);*/

}

return convertView;

}

public class ViewHolder

{

ImageView imageView;

TextView textView;

}

コード内のコメントには、画像読み込みライブラリ「Picasso」を使用する例も示しています。PicassoはAndroid向けの強力なオープンソースの画像ダウンロード&キャッシュライブラリで、アプリへの負荷が軽いのが特徴です。一方、「Glide」はGIF対応や柔軟なカスタマイズ性、優れたメモリ管理が魅力で、より高度な制御が必要な場合に適しています。用途に応じて使い分けるとよいでしょう。

ViewHolderクラスの役割とは?

ViewHolderクラスは、ViewHolderインスタンスを生成し、初回描画時にビュー項目へ紐付ける役割を担います。以降の呼び出しでは、ビューのタグからそのインスタンスが再利用される仕組みです。

getView()メソッドは、ListViewに多数の要素が含まれる場合など、非常に頻繁に呼び出されます。そこで、配列リストの要素を簡単に取得できるよう、setter/getterを持つデータモデルクラスを実装しておくと、今後の開発が格段に楽になります。

6. データモデルクラスを作成する

public class AlbumData

{

int albumId;

int id;

String title;

String url;

String thumbnailUrl;

public int getAlbumId()

{

return albumId;

}

public void setAlbumId(int albumId)

{

this.albumId = albumId;

}

public int getId()

{

return id;

}

public void setId(int id)

{

this.id = id;

}

public String getTitle()

{

return title;

}

public void setTitle(String title)

{

this.title = title;

}

public String getUrl()

{

return url;

}

public void setUrl(String url)

{

this.url = url;

}

public String getThumbnailUrl()

{

return thumbnailUrl;

}

public void setThumbnailUrl(String thumbnailUrl)

{

this.thumbnailUrl = thumbnailUrl;

}

}

まとめ

以上が、AndroidアプリにAPIフェッチャーを組み込むために必要な一連の実装です。レイアウトXMLの作成から、AsyncTaskによるデータ取得、ServiceHandlerでのHTTP通信、カスタムアダプター、ViewHolderパターン、そしてデータモデルクラスまで、すべて整いました。完成したら、BlueStacksなどのAndroidエミュレーターで動作を確認してみてください。APIから取得したデータがリストに正しく表示されれば成功です。

  1. AndroidスマホでGoogleアプリがクラッシュし続ける問題の解決方法

    Androidスマートフォンで「Googleアプリがクラッシュする」という現象が多発しています。 Android端末で動作する数あるアプリの中に、まさかGoogle純正アプリが含まれる日が来るとは思いもしませんでした。Android OSそのものを開発したのはGoogleでありながら、この不具合は特定メーカーのスマホに限らず、Samsung、Xiaomi、さらにはGoogle自社のPixelまで幅広い機種で報告されています。本記事では、AndroidスマホでGoogleアプリが繰り返しクラッシュする問題の解決方法を詳しく解説します。 Googleアプリがクラッシュする原因とは? 解決策に入る

  2. Androidで大切な写真を非表示にする方法|「Keep Photos Secret」で写真・動画をしっかり保護

    スマホの中には、人に見られたくない写真や動画がありませんか?家族や友人にスマホを渡したとき、勝手にギャラリーを見られるのが不安…そんな経験は誰にでもあるものです。実は、Androidスマートフォンなら専用アプリを1つ入れるだけで、大切な思い出を簡単に隠せます。この記事では、無料アプリ「Keep Photos Secret」を使ってAndroidの写真を非表示にする方法を詳しく解説します。 写真を秘密に:「Keep Photos Secret」でギャラリーの画像・動画をロック 最近見つけた便利なアプリのひとつが、Systweak Softwareが開発した「Keep Photos Secret」