Day 22 : Twitter4j

Day 22 : Twitter4j Cover Image

สวัสดีครับ บทความนี้เป็นบทความที่ 22 แล้วนะครับ ที่ผมจะมาเขียน ในซีรีย์ Learn 30 Android Libraries in 30 days

สำหรับบทความทั้งหมด อ่านได้จากด้านล่างครับ

สำหรับวันนี้ขอนำเสนอเรื่อง Twitter4J เป็น Java Library ที่เอาไว้จัดการกับ Twitter API ตัวนี้ไม่ใช่ตัว Official นะครับ ถูกสร้างมาเพื่อให้ใช้งาน Twitter API ได้ง่ายขึ้นและสะดวกขึ้น จุดเด่นคือ

  • Java 100%
  • รองรับ OAuth
  • รองรับ Twitter API เวอร์ชัน 1.1

Installation

เปิดไฟล์ build.gradle ขึ้นมา แล้วเพิ่ม dependencies ลงไปดังนี้

dependencies {
    compile 'org.twitter4j:twitter4j-core:4.0.2'
}

กด Sync Gradle เป็นอันเรียบร้อย

Usage

สำหรับขั้นตอนการใช้งาน Twitter4J ขั้นแรกนั้น เราต้องทำการ config ซะก่อนครับ วิธีการ Configuration นั้นทำได้หลายวิธี แต่สำหรับผม ขอเลือกใช้วิธี config ผ่าน ConfigurationBuilder แบบนี้ครับ

ConfigurationBuilder cb = new ConfigurationBuilder();
cb.setDebugEnabled(true)
  .setOAuthConsumerKey("")
  .setOAuthConsumerSecret("******************************************")
  .setOAuthAccessToken("**************************************************")
  .setOAuthAccessTokenSecret("******************************************");
TwitterFactory tf = new TwitterFactory(cb.build());
Twitter twitter = tf.getInstance();

ด้านบนเป็นตัวอย่าง การ config ค่า โดยใช้ค่า OAuthConsumerKey, OAuthConsumerSecret, OAuthAccessToken และ OAuthAccessTokenSecret ของ Twitter App คุณเอง โดยจะได้เมื่อเราทำการสร้าง Twitter Client App แล้ว

Create Twitter App

วิธีการสร้าง Twitter App ให้เข้าไปที่ Twitter Apps แล้วเลือก Create New App หรือจะกดลิงค์นี้เลยก็ได้ Create new App

Create App

ให้ทำการใส่ข้อมูลที่ต้องการ

  • Name : ต้องไม่ซ้ำกับแอพคนอื่น
  • Description: จะใส่อะไรไปก็ได้ อธิบายไว้ซักหน่อยก็ดี
  • Website: ใส่ชื่อเว็บไซต์ (หากไม่มีก็ใส่อะไรไปก็ได้)
  • Callback: ปล่อยว่างไว้

เลือก Yes, I agree แล้วก็ Create App ซะ

เมื่อเราสร้างแอพเสร็จแล้ว ให้เลือกที่แท็บ API Keys สังเกตจะเห็น API Key กับ API Secret ของเรา นั่นแหละครับ ส่วนนี้ที่เราจะเอาไปใช้

API Key

ต่อมา เรายังขาดอีก 2 Key ใช่มั้ย เลื่อนลงมาอีกนิดนึง กด Create my access token ทีนี้เราก็จะได้ Access token มาแล้ว ครบ 4 ทีนี้ก็เอาไปใส่ในที่เรา เว้นไว้เลยครับ

Access TOken

นำค่าทั้งสี่ค่า มาใส่ที่ ConfigurationBuilder รูปแบบก็จะได้ดังนี้

private static final String API_KEY = "YOUR_API_KEY";
private static final String API_SECRET = "YOUR_SECRET_KEY";
private static final String ACCESS_TOKEN = "YOUR_ACCESS_TOKEN";
private static final String ACCESS_TOKEN_SECRET = "YOUR_ACCESS_TOKEN_SECRET";

ConfigurationBuilder cb = new ConfigurationBuilder();
cb.setDebugEnabled(true)
        .setOAuthConsumerKey(API_KEY)
        .setOAuthConsumerSecret(API_SECRET)
        .setOAuthAccessToken(ACCESS_TOKEN)
        .setOAuthAccessTokenSecret(ACCESS_TOKEN_SECRET);
TwitterFactory tf = new TwitterFactory(cb.build());
Twitter twitter = tf.getInstance();

Create Project

ลองสร้างโปรเจ็คกันดูเลยดีกว่า ตัวโปรเจ็คผมจะมี ListView ไว้แสดง tweet จากหน้า Timeline และก็มี EditText อันนึง เอาไว้ใส่ข้อความที่ต้องการ Tweet ครับ หน้าเลเอาท์ เลยจัดแบบนี้ (ไม่ได้ปรับแต่งอะไรเลย เน้นไวไว้ก่อนครับ) ไฟล์ activity_twitter4j.xml มีดังนี้

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:padding="16dp"
              android:layout_width="match_parent"
              android:layout_height="match_parent">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="16dp">

        <EditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/text_tweet"
            android:layout_alignParentTop="true"
            android:layout_alignParentLeft="true"
            android:layout_toLeftOf="@+id/button_tweet" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/tweet"
            android:id="@+id/button_tweet"
            android:layout_alignParentRight="true"/>

        </RelativeLayout>

    <ListView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/list_view">
    </ListView>

</LinearLayout>

ไฟล์ res/values/strings.xml

<resources>
    <string name="tweet">Tweet</string>
</resources>

ส่วนไฟล์ Twitter4JActivity.java ก็สร้างดังนี้

package com.devahoy.learn30androidlibraries.day22;

import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

import com.devahoy.learn30androidlibraries.R;

import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.conf.ConfigurationBuilder;

public class Twitter4jActivity extends ActionBarActivity {

    private static final String TAG = Twitter4jActivity.class.getSimpleName();

    private static final String API_KEY = "YOUR_API_KEY";
    private static final String API_SECRET = "YOUR_SECRET_KEY";
    private static final String ACCESS_TOKEN = "YOUR_ACCESS_TOKEN";
    private static final String ACCESS_TOKEN_SECRET = "YOUR_ACCESS_TOKEN_SECRET";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.day22_activity_twitter4j);

        ConfigurationBuilder cb = new ConfigurationBuilder();
        cb.setDebugEnabled(true)
                .setOAuthConsumerKey(API_KEY)
                .setOAuthConsumerSecret(API_SECRET)
                .setOAuthAccessToken(ACCESS_TOKEN)
                .setOAuthAccessTokenSecret(ACCESS_TOKEN_SECRET);
        TwitterFactory factory = new TwitterFactory(cb.build());

        Twitter twitter = factory.getInstance();
        try {
            twitter.updateStatus("Learn to create Simple Tweet with Twitter4J via @Devahoy");
        } catch (TwitterException e) {
            Log.d(TAG, e.toString());
        }

    }
}

โค๊ดด้านบน เมื่อเราทำการสร้าง TwitterFactory จาก ConfigurationBuilder แล้ว ก็สั่งให้ factory.getInstance().updateStatus() เพื่อทำการโพสลง Twitter ครับ ลองรันโปรแกรมดูเลย เมื่อเปิดแอพมา จะมีข้อความไปโผล่ใน Tweet แบบที่ตั้งใจไว้หรือไม่?

ปรากฎว่ามี Error ครับ แอพรันไม่ได้

401:Authentication credentials (https://dev.twitter.com/pages/auth) were missing or incorrect. 
Ensure that you have set valid consumer key/secret, access token/secret, and the system clock is in sync.
    {"request":"\/1.1\/statuses\/update.json","error":"Read-only application cannot POST."}
    Relevant discussions can be found on the Internet at:
    http://www.google.co.jp/search?q=2fc5b7cb or
    http://www.google.co.jp/search?q=0e1dbbf5
    TwitterException{exceptionCode=[2fc5b7cb-0e1dbbf5], statusCode=401, 
    message=null, code=-1, retryAfter=-1, rateLimitStatus=null, version=4.0.2}

อะฮ่า 401:Authentication credentials, Read-only application cannot POST. คงเดากันได้ไม่ยากครับ คือไม่มีสิทธิ์เข้าถึงข้อมูล เพราะอะไร? ลองกลับไปดูที่ส่วน Application Settings ในหน้าเว็บดูครับ สังเกตที่ Access Level เราตั้งให้มันแค่ Read-Only ฉะนั้นแอพนี้มีสิทธิ์แค่ อ่าน Tweet จาก Timeline ได้เท่านั้น ไม่มีสิทธิ์ โพส Tweet หากเราต้องการให้มัน tweet ได้ด้วย ก็ต้องไปเปลี่ยน Access Level เป็น Read and Write ครับ

เลือกเปลี่ยน Modify app permission เป็น Read and Write และทำการ Regenerate API Keys และ Regenerate my access token key ใหม่ด้วย เพื่อให้แน่ใจว่าได้สิทธิ์ Read and Write แล้ว

Read and Write

จากนั้นก็ใช้ API Keys อันใหม่ ไปแทนของเก่าครับ ทดสอบรันแอพอีกครั้ง คราวนี้ Error Network OnmainThreadException ครับ ต้องใช้ AsyncTask มาช่วย

package com.devahoy.learn30androidlibraries.day22;

import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

import com.devahoy.learn30androidlibraries.R;

import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.conf.ConfigurationBuilder;

public class Twitter4jActivity extends ActionBarActivity {

    private static final String TAG = Twitter4jActivity.class.getSimpleName();

    private static final String API_KEY = "YOUR_API_KEY";
    private static final String API_SECRET = "YOUR_SECRET_KEY";
    private static final String ACCESS_TOKEN = "YOUR_ACCESS_TOKEN";
    private static final String ACCESS_TOKEN_SECRET = "YOUR_ACCESS_TOKEN_SECRET";

    private TwitterFactory mFactory;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.day22_activity_twitter4j);

        ConfigurationBuilder cb = new ConfigurationBuilder();
        cb.setDebugEnabled(true)
                .setOAuthConsumerKey(API_KEY)
                .setOAuthConsumerSecret(API_SECRET)
                .setOAuthAccessToken(ACCESS_TOKEN)
                .setOAuthAccessTokenSecret(ACCESS_TOKEN_SECRET);
        mFactory = new TwitterFactory(cb.build());

        new AsyncTweet().execute();
    }

    private class AsyncTweet extends AsyncTask<Void, Void, String> {
        @Override
        protected String doInBackground(Void... params) {

            String result = null;
            Twitter twitter = mFactory.getInstance();
            try {
                twitter.updateStatus("Learn to create Simple Tweet with Twitter4J via @Devahoy");
            } catch (TwitterException e) {
                Log.d(TAG, e.toString());
                result = e.getErrorMessage();
            }
            return result;
        }

        @Override
        protected void onPostExecute(String result) {
            super.onPostExecute(result);
            if (result == null) {
                Toast.makeText(Twitter4jActivity.this, "Tweeted!", Toast.LENGTH_LONG).show();
            }

        }
    }
}

ทีนี้ก็ไม่มี error แล้ว พอเปิดดูหน้า Twitter ผม ได้ข้อความนี้ขึ้นมา

ทีนี้ เรามี EditText และ Button นี่นา ก็ใช้มันกรอกข้อความ จากนั้นก็กด Button ถึง Tweet ดีกว่าเปิดแอพมาแล้ว Tweet ก็เลยจัดการ เปลี่ยนเป็นให้โพส Tweet เมื่อมีการกดปุ่ม Button เท่านั้น ก็เพิ่มนี้ลงไป

public class Twitter4jActivity extends ActionBarActivity {

    private EditText mTweet;
    private Button mButtonTweet;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.day22_activity_twitter4j);

        mTweet = (EditText) findViewById(R.id.text_tweet);
        mButtonTweet = (Button) findViewById(R.id.button_tweet);

        ConfigurationBuilder cb = ...
        ...

        mButtonTweet.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new AsyncTweet().execute();
            }
        });
    }
}

สุดท้ายแล้ว ListView เอาไว้แสดง Timeline จาก Twitter ก็ใช้คำสั่งนี้ครับ

Twitter twitter = mFactory.getInstance();
List<twitter4j.Status> statuses = twitter.getHomeTimeline();
for (twitter4j.Status status : statuses) {
    // status.getUser().getName()
}

สร้าง AsyncTask อีกอันสำหรับ Timeline

public class Twitter4jActivity extends ActionBarActivity {

    private ListView mListView;
    private ArrayList<String> mTimelines = new ArrayList<String>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.day22_activity_twitter4j);

        mListView = (ListView) findViewById(R.id.list_view);
        ....

        new AsyncTimeline().execute();
    }

    private class AsyncTimeline extends AsyncTask<Void, Void, String> {
        @Override
        protected String doInBackground(Void... params) {

            Twitter twitter = mFactory.getInstance();
            String result = null;
            try {
                List<twitter4j.Status> statuses = twitter.getHomeTimeline();
                for (twitter4j.Status status : statuses) {
                    mTimelines.add(status.getUser().getName() + " : " +
                            status.getText());
                }

            } catch (TwitterException e) {
                Log.d(TAG, e.toString());
                result = e.getErrorMessage();
            }

            return result;
        }

        @Override
        protected void onPostExecute(String s) {
            super.onPostExecute(s);

            if (s == null) {
                ArrayAdapter<String> adapter =
                        new ArrayAdapter<String>(Twitter4jActivity.this,
                                android.R.layout.simple_list_item_1, mTimelines);

                mListView.setAdapter(adapter);
            }
        }
    }

จากโค๊ดด้านบน ทำการสร้าง AsyncTimeline ซึ่ง extend AsyncTask เอาไว้ดึงข้อมูล Timeline มา จากนั้นผมก็นำมาใส่ใน mTimeline ซึ่งเป็น ArrayList<String> จากนั้นก็นำมาแสดงใน ListView

เมื่อเปิดแอพดู ก็จะได้หน้าตาแบบนี้

Result

ไฟล์ของบทความนี้ Twitter4jActivity.java ลองเอาไปรัน และลองทดสอบเล่นกันดูนะครับ ส่วนรายละเอียดต่างๆ อ่านจากเว็บต้นฉบับ มีวิธการตั้งค่า การใช้งานอยู่ครบถ้วนครับ

Happy Coding :D

Reference

Chai Chai Phonbopit : Web Developer @Nimbl3 • ผู้ชายธรรมดาๆ ที่ชื่นชอบ Node.js, JavaScript และ Open Source มีงานอดิเรกเป็น Acoustic Guitar และ Football

บทความล่าสุด