i am rewriting robospice retrofit module with retrofit 2 .
the problem that i have are in 2 methods(saveData , readCacheDataFromFile) :
import com.octo.android.robospice.persistence.exception.CacheCreationException;
import com.octo.android.robospice.persistence.exception.CacheLoadingException;
import com.octo.android.robospice.persistence.exception.CacheSavingException;
import com.octo.android.robospice.persistence.file.InFileObjectPersister;
public class RetrofitObjectPersister<T> extends InFileObjectPersister<T> {
// ============================================================================================
// ATTRIBUTES
// ============================================================================================
private final Converter converter;
// ============================================================================================
// CONSTRUCTOR
// ============================================================================================
public RetrofitObjectPersister(Application application, Converter converter, Class<T> clazz, File cacheFolder) throws CacheCreationException {
super(application, clazz, cacheFolder);
this.converter = converter;
}
public RetrofitObjectPersister(Application application, Converter converter, Class<T> clazz) throws CacheCreationException {
this(application, converter, clazz, null);
}
// ============================================================================================
// METHODS
// ============================================================================================
#Override
public T saveDataToCacheAndReturnData(final T data, final Object cacheKey) throws CacheSavingException {
try {
if (isAsyncSaveEnabled()) {
Thread t = new Thread() {
#Override
public void run() {
try {
saveData(data, cacheKey);
} catch (IOException e) {
Ln.e(e, "An error occured on saving request " + cacheKey + " data asynchronously");
} catch (CacheSavingException e) {
Ln.e(e, "An error occured on saving request " + cacheKey + " data asynchronously");
}
};
};
t.start();
} else {
saveData(data, cacheKey);
}
} catch (CacheSavingException e) {
throw e;
} catch (Exception e) {
throw new CacheSavingException(e);
}
return data;
}
private void saveData(T data, Object cacheKey) throws IOException, CacheSavingException {
// transform the content in json to store it in the cache
TypedOutput typedBytes = converter.toBody(data);
FileOutputStream out = null;
try {
out = new FileOutputStream(getCacheFile(cacheKey));
typedBytes.writeTo(out);
} finally {
if (out != null) {
out.close();
}
}
}
#SuppressWarnings("unchecked")
#Override
protected T readCacheDataFromFile(File file) throws CacheLoadingException {
InputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream(file);
final byte[] body = IOUtils.toByteArray(fileInputStream);
TypedInput typedInput = new TypedInput() {
#Override
public String mimeType() {
return "application/json";
}
#Override
public long length() {
return body.length;
}
#Override
public InputStream in() throws IOException {
return new ByteArrayInputStream(body);
}
};
return (T) converter.fromBody(typedInput, getHandledClass());
} catch (FileNotFoundException e) {
// Should not occur (we test before if file exists)
// Do not throw, file is not cached
Ln.w("file " + file.getAbsolutePath() + " does not exists", e);
return null;
} catch (Exception e) {
throw new CacheLoadingException(e);
} finally {
IOUtils.closeQuietly(fileInputStream);
}
}
}
how can i rewrite these methods in retrofit 2 ?
what is the equivalent of TypedOutput and TypedInput interfaces in retrofit 2 ?
i asked Jake Wharton about this , and he replied that The equivalents are :
RequestBody and ResponseBody
Related
I've use this code to download Json file and convert it to my project.
Since there are 40k items to download I need to add a % of the download of this file.
My code is:
final Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.baseUrl(AirportService.SERVICE_ENDPOINT).build();
AirportService service = retrofit.create(AirportService.class);
service.getAirport()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<List<Airport>>()
{
#Override
public void onCompleted()
{
}
#Override
public void onError(Throwable e)
{
e.printStackTrace();
}
#Override
public void onNext(List<Airport> airports)
{
airps = airports;
}
});
but I'm at the beginning of retrofit and I don't know to do it.
Anyone could help me?
Thanks
Here's some code that I used to accomplish something similar. In my example, I had to post JSON, then download and parse a JSON response and store the response in a SqLite database. You will need to adapt my code to your needs, but the concept is simple. The solution basically boils down to:
1) Parse the Json yourself using a JsonReader.
2) Use a FilterInputStream to publish progress.
I recognize that this solution doesn't use retrofit, so maybe you won't want to use it, but perhaps you will find it helpful.
final ProgressDialog dialog = new ProgressDialog(getActivity(), ProgressDialog.THEME_HOLO_LIGHT);
dialog.setTitle(R.string.downloading_data);
dialog.setMessage("Downloading data...");
dialog.setCanceledOnTouchOutside(false);
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
if (!BuildConfig.DEBUG) {
dialog.setProgressNumberFormat(null);
}
mLoginTask = new AsyncTask<Void, Integer, Void>() {
#Override
protected Void doInBackground(Void... params) {
HttpURLConnection connection = null;
try {
byte[] request = ...; // I used a JsonWriter to write to a ByteArrayOutputStream. Since you're likely getting this from file or a GET request, this is likely not necessary.
URL url = new URL("https://example.com/endpoint");
connection = (HttpURLConnection)url.openConnection();
connection.setDoOutput(true);
connection.setUseCaches(false);
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/json; charset=utf-8");
connection.setRequestProperty("Content-Length", "" + request.length);
connection.setRequestProperty("Accept","*/*");
OutputStream out = connection.getOutputStream();
out.write(request);
out.flush();
out.close();
connection.setConnectTimeout(3000);
connection.setReadTimeout(10000);
connection.connect();
Thread.sleep(2000); // I don't remember why this was necessary. Likely an exception was thrown in the next line without it, at least on slower networks.
int responseCode = connection.getResponseCode();
switch (responseCode) {
case 200:
int length = connection.getContentLength();
publishProgress(0, length);
final InputStreamReader streamReader = new InputStreamReader(new FilterInputStream(connection.getInputStream()) {
private int mTotalBytesRead;
#Override
public int read() throws IOException {
int b = super.read();
if (b > 0) {
updateProgress(1);
}
return b;
}
#Override
public int read(byte[] buffer) throws IOException {
return updateProgress(super.read(buffer));
}
#Override
public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
return updateProgress(super.read(buffer, byteOffset, byteCount));
}
#Override
public long skip(long byteCount) throws IOException {
return updateProgress(super.skip(byteCount));
}
#Override
public void mark(int readLimit) {
throw new UnsupportedOperationException();
}
#Override
public void reset() throws IOException {
throw new UnsupportedOperationException();
}
#Override
public boolean markSupported() {
return false;
}
private int updateProgress(long numBytesRead) {
if (numBytesRead > 0) {
publishProgress(mTotalBytesRead += numBytesRead);
}
return (int)numBytesRead;
}
});
JsonReader reader = new JsonReader(streamReader);
// your code here will depend on the format of the code. It seems like you have simply an array.
reader.beginArray();
while (reader.peek() == JsonToken.BEGIN_OBJECT) {
JsonObject airport = (JsonObject) Streams.parse(reader);
// do some stuff with this airport. Perhaps create an Airport object and add it to an ArrayList.
}
return null;
default:
String response = IOUtils.toString(connection.getErrorStream());
try {
Util.showToast(JsonUtil.getString(new JSONObject(response), Constants.ERROR), Toast.LENGTH_LONG);
if (responseCode == 403) { // Forbidden
publishProgress();
}
} catch (Exception e) {
Util.showToast("Unsupported response\n" + connection.getHeaderField(null)+"\n"+response, Toast.LENGTH_LONG);
}
break;
}
} catch (UnknownHostException e) {
Util.showToast("You don't appear to be connected to the Internet.", Toast.LENGTH_LONG);
} catch (Exception e) {
if (e instanceof InterruptedIOException) {
return null;
}
Util.showToast(e.toString(), Toast.LENGTH_LONG);
Log.e("DashboardActivity", "error downloading data", e);
} finally {
if (connection != null) {
connection.disconnect();
}
}
return null;
}
#Override
protected void onPostExecute(Void result) {
dialog.cancel();
TaskUtil.synchronize(HomeFragment.this);
}
#Override
protected void onCancelled() {
dialog.cancel();
// todo: maybe you have more cleanup
}
#Override
protected void onProgressUpdate(Integer... values) {
if (values.length == 0) {
MyActivity.getCurrentActivity().showLoginInfo(true);
} else {
dialog.setProgress(values[0]);
if (values.length == 2) {
dialog.setMax(values[1]);
dialog.setIndeterminate(false);
dialog.show();
}
}
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
#Override
public void onCancel(DialogInterface dialog) {
mLoginTask.cancel(true);
}
});
I am trying to parse twitter/trends/place api to my android app with retrofit, however, i have been able to parse the api below with retrofit and i get all my desired data:
https://api.twitter.com/1.1/search/tweets.json,
am using the same approach for https://api.twitter.com/1.1/trends/place.json, but i am getting this exception
GSON throwing “Expected BEGIN_OBJECT but was BEGIN_ARRAY
Below is my code. i believe my error is coming from one of the code below, some should please help me.
TwitterApiService.java
public interface TwitterApiService {
#GET(ApiConstants.TWITTER_TREND_SEARCH_CODE )
void getTweetTagList(
#Header("Authorization") String authorization,
#Query("id") String id,
Callback<TweetTagList> callback
);
#FormUrlEncoded
#POST("/oauth2/token")
void getToken(
#Header("Authorization") String authorization,
#Field("grant_type") String grantType,
Callback<TwitterTokenType> response
);
}
TwitterServiceProvider.java
public class TwitterServiceProvider {
private static final String TAG = TwitterServiceProvider.class.getName();
private TwitterApiService mApi;
private Bus mBus;
public TwitterServiceProvider(TwitterApiService api, Bus bus) {
this.mApi = api;
this.mBus = bus;
}
#Subscribe
public void onLoadTweets(final SearchTweetsTagEvent event) {
mApi.getTweetTagList("Bearer " + event.twitterToken, event.id, new Callback<TweetTagList>() {
#Override
public void success(TweetTagList response, Response rawResponse) {
mBus.post(new SearchTweetsTagEventOk(response));
}
#Override
public void failure(RetrofitError error) {
Log.e(TAG, error.toString(), error);
mBus.post(new SearchTweetsEventFailed());
}
});
}
#Subscribe
public void onGetToken(TwitterGetTokenEvent event) {
try {
mApi.getToken("Basic " + getBase64String(ApiConstants.BEARER_TOKEN_CREDENTIALS), "client_credentials", new Callback<TwitterTokenType>() {
#Override
public void success(TwitterTokenType token, Response response) {
PrefsController.setAccessToken(TwitterSearchApplication.getAppContext(), token.accessToken);
PrefsController.setTokenType(TwitterSearchApplication.getAppContext(), token.tokenType);
mBus.post(new TwitterGetTokenEventOk());
}
#Override
public void failure(RetrofitError error) {
Log.e(TAG, error.toString(), error);
mBus.post(new TwitterGetTokenEventFailed());
}
});
} catch (UnsupportedEncodingException e) {
Log.e(TAG, e.toString(), e);
}
}
/*private static String getResponseBody(InputStream inputStream) {
StringBuilder sb = new StringBuilder();
BufferedReader bReader = null;
try {
bReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"), 8);
String line = null;
while ((line = bReader.readLine()) != null) {
sb.append(line);
}
} catch (UnsupportedEncodingException ex) {
Log.e("LOG", "", ex);
} catch (ClientProtocolException ex1) {
Log.e("LOG", "", ex1);
} catch (IOException ex2) {
Log.e("LOG", "", ex2);
}
return sb.toString();
}*/
/*// converts a string of JSON data into a Twitter object
private static TweetList jsonToTweetLost(String result) {
TweetList twits = null;
if (result != null && result.length() > 0) {
try {
Gson gson = new Gson();
twits = gson.fromJson(result, TweetList.class);
} catch (IllegalStateException ex) {
Log.e("LOG", "",ex);
}
}
return twits;
}*/
}
Tweet.java
public class Tweet {
#SerializedName("created_at")
public String dateCreated;
#SerializedName("trends")
public TweetTag trend;
#Override
public String toString(){
return trend.nameTag;
}
}
TweetTagList.java
public class TweetList {
#SerializedName("")
public ArrayList<Tweet> tweets;
}
SearchTweetsTagEvent.java
public class SearchTweetsTagEvent {
public final String id;
public final String twitterToken;
public SearchTweetsTagEvent(String twitterToken, String hashtag) {
this.id = hashtag;
this.twitterToken = twitterToken;
}
}
Looks like you are using wrong model object for JSON response parsing. You can choose the proper one from twitter-kit-android. If I understand correctly Place.java is what you are looking for.
I use the following code to catch a global uncaught error, the test code System.out.println(s.equals("any string")); will cause an error.
In my mind, one error log file will be created, but in fact, the three error log files were created with same content, what problem are there in my code?
BTW, I test the code in Android 4.0, only one error log file was generated! but when it run under Android 5.0, three error log files was generated!
MainActivity.java
public class MainActivity extends AppCompatActivity {
private String s;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
System.out.println(s.equals("any string"));
}
}
CrashApplication.java
public class CrashApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
CrashHandler crashHandler = CrashHandler.getInstance();
crashHandler.init(getApplicationContext());
}
}
CrashHandler.java
public class CrashHandler implements UncaughtExceptionHandler {
public static final String TAG = "CrashHandler";
private Thread.UncaughtExceptionHandler mDefaultHandler;
private static CrashHandler INSTANCE = new CrashHandler();
private Context mContext;
private Map<String, String> infos = new HashMap<String, String>();
private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
private CrashHandler() {
}
public static synchronized CrashHandler getInstance() {
return INSTANCE;
}
public void init(Context context) {
mContext = context;
mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(this);
}
#Override
public void uncaughtException(Thread thread, Throwable ex) {
if (!handleException(ex) && mDefaultHandler != null) {
mDefaultHandler.uncaughtException(thread, ex);
} else {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
Log.e(TAG, "error : ", e);
}
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(1);
}
}
private boolean handleException(Throwable ex) {
if (ex == null) {
return false;
}
new Thread() {
#Override
public void run() {
Looper.prepare();
Toast.makeText(mContext, "Sorry.", Toast.LENGTH_LONG).show();
Looper.loop();
}
}.start();
collectDeviceInfo(mContext);
saveCrashInfo2File(ex);
return true;
}
public void collectDeviceInfo(Context ctx) {
try {
PackageManager pm = ctx.getPackageManager();
PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES);
if (pi != null) {
String versionName = pi.versionName == null ? "null" : pi.versionName;
String versionCode = pi.versionCode + "";
infos.put("versionName", versionName);
infos.put("versionCode", versionCode);
}
} catch (NameNotFoundException e) {
Log.e(TAG, "an error occured when collect package info", e);
}
Field[] fields = Build.class.getDeclaredFields();
for (Field field : fields) {
try {
field.setAccessible(true);
infos.put(field.getName(), field.get(null).toString());
Log.d(TAG, field.getName() + " : " + field.get(null));
} catch (Exception e) {
Log.e(TAG, "an error occured when collect crash info", e);
}
}
}
private String saveCrashInfo2File(Throwable ex) {
StringBuffer sb = new StringBuffer();
for (Map.Entry<String, String> entry : infos.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
sb.append(key + "=" + value + "\n");
}
Writer writer = new StringWriter();
PrintWriter printWriter = new PrintWriter(writer);
ex.printStackTrace(printWriter);
Throwable cause = ex.getCause();
while (cause != null) {
cause.printStackTrace(printWriter);
cause = cause.getCause();
}
printWriter.close();
String result = writer.toString();
sb.append(result);
try {
long timestamp = System.currentTimeMillis();
String time = formatter.format(new Date());
String fileName = "crash-" + time + "-" + timestamp + ".txt";
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
String path = Environment.getExternalStorageDirectory() + "/MyCrash/";
File dir = new File(path);
if (!dir.exists()) {
dir.mkdirs();
}
FileOutputStream fos = new FileOutputStream(path + fileName);
fos.write(sb.toString().getBytes());
fos.close();
}
return fileName;
} catch (Exception e) {
Log.e(TAG, "an error occured while writing file...", e);
}
return null;
}
}
There are different log files in android: Error,Debug,Verbose etc.
And therefore there are different qualifiers for them and they are Log.e,Log.d,Log.v etc. Two of them you are using e and d qualifiers but I don't know about third
I have a method startSignalR calling
mHub.on("broadcastMessage",
new SubscriptionHandler1<CustomMessage>() {
#Override
public void run(final CustomMessage msg) {
final String finalMsg = msg.UserName + ": " + msg.Message;
System.out.println(finalMsg);
}
}
, CustomMessage.class);
Now I wanna a dynamic class (not only CustomMessage) passed to the method, I have used Class <T> tClass as parameter of "startSignalR", but get error at
new SubscriptionHandler1<tClass>()
Would you please tell me how to solve this?
I have found a solution
public <T> void startSignalR(String transport, String serverUrl, final String userName, final Class<T> tClass) {
Platform.loadPlatformComponent(new AndroidPlatformComponent());
Credentials credentials = new Credentials() {
#Override
public void prepareRequest(Request request) {
request.addHeader(HEADER_KEY_USERNAME, userName);
}
};
mConnection = new HubConnection(serverUrl);
mConnection.setCredentials(credentials);
mHub = mConnection.createHubProxy(SERVER_HUB_CHAT);
if (transport.equals("ServerSentEvents")) {
mTransport = new ServerSentEventsTransport(mConnection.getLogger());
} else if (transport.equals("LongPolling")) {
mTransport = new LongPollingTransport(mConnection.getLogger());
}
mAwaitConnection = mConnection.start(mTransport);
try {
mAwaitConnection.get();
} catch (InterruptedException e) {
e.printStackTrace();
return;
} catch (ExecutionException e) {
e.printStackTrace();
return;
}
mHub.on("broadcastMessage",
new SubscriptionHandler1<Object>() {
#Override
public void run(final Object msg) {
final String finalMsg;
Gson gson = new Gson();
Object object = gson.fromJson(msg.toString(), tClass);
Field[] fields = object.getClass().getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
try {
System.out.println("Value = " + fields[i].get(object));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
, Object.class);
...
}
public class HttpProxy{
public static <T> T getProxy(Class<T> c) {
return (T) Proxy.newProxyInstance(HttpProxy.class.getClassLoader(),
new Class<?>[] { c }, handler);
}
static class HttpInvocationHandler implements InvocationHandler {
#Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object obj = null;
try {
throw new CustomException()
}catch (CustomException e) {
//it work here
throw e;
}
return obj;
}
}
}
call the method:
try{
Res resp = HttpProxy.getProxy(IMODEL.class).login();
if (res != null){
return ok;
}
} catch (CustomException e) {
//after set proguard.config ,its didn't work
Log.e(TAG, Log.getStackTraceString(e));
} catch(Exception e){
//after set proguard.config ,its did here
}
If I set project.properties , I can't catch custom exception.
If I don't not set project.properties it works fine.
like this obfuscation with proguard vs. java.lang.reflect.Proxy