XDA is an awesome place for developers to share their knowledge and help each other, no question about it.
However, in my opinion, one of the flaws that prevent the app development forums from being more popular is the
Code:
tags.
Indeed, these tags do not provide syntax highlighting, and when people start pasting their whole Java class, the lack of proper indentation plus the lack of syntax highlighting leaves us with a very off-putting chunk of code, and even if you intend to help in the first place, having to go through this unreadable code can discourage many.
I was ranting about this in the [URL="http://forum.xda-developers.com/showpost.php?p=49938207&postcount=1976"]xda suggestions & feedback thread[/URL], when someone pointed-out the existence of the [B][PHP][/B] tags, which provide syntax highlighting for php, and, the syntaxes of PHP and Java being somewhat similar, they also do the trick for Java.
[FONT="Garamond"][I]Example:[/I][/FONT]
[PHP]
package com.androguide.recovery.emulator;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStream;
import android.util.Log;
public class CMDProcessor {
private final String TAG = getClass().getSimpleName();
private static final boolean DEBUG = false;
private Boolean can_su;
public SH sh;
public SH su;
public CMDProcessor() {
sh = new SH("sh");
su = new SH("su");
}
public SH suOrSH() {
return canSU() ? su : sh;
}
public boolean canSU() {
return canSU(false);
}
public class CommandResult {
private final String resultTag = TAG + '.' + getClass().getSimpleName();
public final String stdout;
public final String stderr;
public final Integer exit_value;
CommandResult(final Integer exit_value_in) {
this(exit_value_in, null, null);
}
CommandResult(final Integer exit_value_in, final String stdout_in,
final String stderr_in) {
exit_value = exit_value_in;
stdout = stdout_in;
stderr = stderr_in;
if (DEBUG)
Log.d(TAG, resultTag + "( exit_value=" + exit_value
+ ", stdout=" + stdout + ", stderr=" + stderr + " )");
}
public boolean success() {
return exit_value != null && exit_value == 0;
}
}
public class SH {
private String SHELL = "sh";
public SH(final String SHELL_in) {
SHELL = SHELL_in;
}
private String getStreamLines(final InputStream is) {
String out = null;
StringBuffer buffer = null;
final DataInputStream dis = new DataInputStream(is);
try {
if (dis.available() > 0) {
buffer = new StringBuffer(dis.readLine());
while (dis.available() > 0) {
buffer.append("\n").append(dis.readLine());
}
}
dis.close();
} catch (final Exception ex) {
Log.e(TAG, ex.getMessage());
}
if (buffer != null) {
out = buffer.toString();
}
return out;
}
public Process run(final String s) {
Process process = null;
try {
process = Runtime.getRuntime().exec(SHELL);
final DataOutputStream toProcess = new DataOutputStream(
process.getOutputStream());
toProcess.writeBytes("exec " + s + "\n");
toProcess.flush();
} catch (final Exception e) {
Log.e(TAG,
"Exception while trying to run: '" + s + "' "
+ e.getMessage());
process = null;
}
return process;
}
public CommandResult runWaitFor(final String s) {
if (DEBUG)
Log.d(TAG, "runWaitFor( " + s + " )");
final Process process = run(s);
Integer exit_value = null;
String stdout = null;
String stderr = null;
if (process != null) {
try {
exit_value = process.waitFor();
stdout = getStreamLines(process.getInputStream());
stderr = getStreamLines(process.getErrorStream());
} catch (final InterruptedException e) {
Log.e(TAG, "runWaitFor " + e.toString());
} catch (final NullPointerException e) {
Log.e(TAG, "runWaitFor " + e.toString());
}
}
return new CommandResult(exit_value, stdout, stderr);
}
}
public boolean canSU(final boolean force_check) {
if (can_su == null || force_check) {
final CommandResult r = su.runWaitFor("id");
final StringBuilder out = new StringBuilder();
if (r.stdout != null) {
out.append(r.stdout).append(" ; ");
}
if (r.stderr != null) {
out.append(r.stderr);
}
Log.d(TAG, "canSU() su[" + r.exit_value + "]: " + out);
can_su = r.success();
}
return can_su;
}
}
[/PHP]
Oddly enough, the [B][PHP][/B] tags also work for XML (while the [html] tags, that do exist, don't work):
[PHP]
<LinearLayout
android:id="@+id/contentLayout"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_marginLeft="8dip"
android:layout_weight="90"
android:orientation="vertical" >
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dip"
android:fontFamily="sans-serif-light"
android:textColor="#33B6EA"
android:textSize="22sp" />
<TextView
android:id="@+id/description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="8dip"
android:layout_marginRight="8dip"
android:ellipsize="end"
android:fontFamily="sans-serif-light"
android:textColor="#232323"
android:maxLines="50"
android:textSize="16sp" />
</LinearLayout>
[/PHP]
This doesn't fix the indentation issues, but as you can see, the code is much more readable and welcoming.
By using the [B][PHP][/B] tags instead of the [strike][CODE][/strike] tags, you will be more likely to get answers to your questions involving code.
[B][SIZE="3"][CENTER]So Please, for the sake of the app development forums and the eyes of your readers:
USE THE [COLOR="RoyalBlue"][PHP][/COLOR] TAGS :victory:[/CENTER][/SIZE][/B]
[CENTER][I]Please raise awareness about this, suggest people who don't use the [B][PHP][/B] tags to use them, and together we'll make this app development forum a better place than it already is :good:[/I][/CENTER]
Whoa! Never knew about this. Nice find.
Wow, this is really cool! Thanks for finding. The only problem I have with the PHP tags is on mobile, tapatalk is obviously not supporting it at all:
SimplicityApks said:
Wow, this is really cool! Thanks for finding. The only problem I have with the PHP tags is on mobile, tapatalk is obviously not supporting it at all:
Click to expand...
Click to collapse
Too bad, thanks for reporting :good:
This obviously is just a workaround, xda could quite easily implement a dedicated syntax highlighting plugin for vBulletin as I suggested in the suggestions thread, but for the time being this is probably the closest thing we've got.
Androguide.fr said:
Too bad, thanks for reporting :good:
This obviously is just a workaround, xda could quite easily implement a dedicated syntax highlighting plugin for vBulletin as I suggested in the suggestions thread, but for the time being this is probably the closest thing we've got.
Click to expand...
Click to collapse
They've got it working way better with the CODE tags: Code syntax highlighting announcement!
Related
Hello,
I've been trying to do some android stuff on java for some time now, and i've come across a problem here: i can't get the app to execute linux stuff, as there is no system() method like on other platforms... so i searched some code and found this:
Code:
protected void system(String[] Commands){
e Process process = Runtime.getRuntime().exec("su");
DataOutputStream os = new DataOutputStream(process.getOutputStream());
DataInputStream osRes = new DataInputStream(process.getInputStream());
Vector<String> res = new Vector<String>();
for (String single : Commands) {
e os.writeBytes(single + "\n");
e os.flush();
e res.add(osRes.readLine());
// Log.e("CMDs", osRes.readLine());
}
e os.writeBytes("exit\n");
e os.flush();
process.waitFor();
}
However that won't work because of some errors i have in the marked lines:
"Unhandled exception type IOException"
and the process.waitFor(); line also gives me an error:
"Unhandled exception type InterruptedException"
Any ideas?
You need to add a try/catch block around that code which catches the IO exception and the interrupted exception.
deleted
So, first of all thanks to both of you it appears to be working now... i tried in on the emulator, and of course "su" didn't work there (broken pipe), so i replaced it by "sh", however this didn't seem to work well too. the application just locked up with a warning in android.... strange...
edit: tried using /system/bin/sh, didn't work, locked up again
What version of Android in the emulator? I've done it with 1.5 through 2.2 in the emulator, just by using "sh".
could you post the code you used please? would be AWESOME!
i'm trying to get this working on 2.1
Sure, I can post some more details later, but for now just the code.
Include the file in your project and use with:
Code:
ShellCommand cmd = new ShellCommand();
CommandResult r = cmd.sh.runWaitFor("ls -l");
if (!r.success()) {
Log.v(TAG, "Error " + r.stderr);
} else {
Log.v(TAG, "Success! " + r.stdout);
}
If you want su you can either use cmd.shOrSu().runWaitFor("..."); which will try su (by running "id", it just tests the status code but it's a nice entry in logcat for debugging) and fallback to sh. Or you can use cmd.su.runWaitFor("...");
Also at
teslacoilsw.com/files/ShellCommand.java
Code:
package com.teslacoilsw.quicksshd;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStream;
import android.util.Log;
public class ShellCommand {
private static final String TAG = "ShellCommand.java";
private Boolean can_su;
public SH sh;
public SH su;
public ShellCommand() {
sh = new SH("sh");
su = new SH("su");
}
public boolean canSU() {
return canSU(false);
}
public boolean canSU(boolean force_check) {
if (can_su == null || force_check) {
CommandResult r = su.runWaitFor("id");
StringBuilder out = new StringBuilder();
if (r.stdout != null)
out.append(r.stdout).append(" ; ");
if (r.stderr != null)
out.append(r.stderr);
Log.v(TAG, "canSU() su[" + r.exit_value + "]: " + out);
can_su = r.success();
}
return can_su;
}
public SH suOrSH() {
return canSU() ? su : sh;
}
public class CommandResult {
public final String stdout;
public final String stderr;
public final Integer exit_value;
CommandResult(Integer exit_value_in, String stdout_in, String stderr_in)
{
exit_value = exit_value_in;
stdout = stdout_in;
stderr = stderr_in;
}
CommandResult(Integer exit_value_in) {
this(exit_value_in, null, null);
}
public boolean success() {
return exit_value != null && exit_value == 0;
}
}
public class SH {
private String SHELL = "sh";
public SH(String SHELL_in) {
SHELL = SHELL_in;
}
public Process run(String s) {
Process process = null;
try {
process = Runtime.getRuntime().exec(SHELL);
DataOutputStream toProcess = new DataOutputStream(process.getOutputStream());
toProcess.writeBytes("exec " + s + "\n");
toProcess.flush();
} catch(Exception e) {
Log.e(QuickSSHD.TAG, "Exception while trying to run: '" + s + "' " + e.getMessage());
process = null;
}
return process;
}
private String getStreamLines(InputStream is) {
String out = null;
StringBuffer buffer = null;
DataInputStream dis = new DataInputStream(is);
try {
if (dis.available() > 0) {
buffer = new StringBuffer(dis.readLine());
while(dis.available() > 0)
buffer.append("\n").append(dis.readLine());
}
dis.close();
} catch (Exception ex) {
Log.e(TAG, ex.getMessage());
}
if (buffer != null)
out = buffer.toString();
return out;
}
public CommandResult runWaitFor(String s) {
Process process = run(s);
Integer exit_value = null;
String stdout = null;
String stderr = null;
if (process != null) {
try {
exit_value = process.waitFor();
stdout = getStreamLines(process.getInputStream());
stderr = getStreamLines(process.getErrorStream());
} catch(InterruptedException e) {
Log.e(TAG, "runWaitFor " + e.toString());
} catch(NullPointerException e) {
Log.e(TAG, "runWaitFor " + e.toString());
}
}
return new CommandResult(exit_value, stdout, stderr);
}
}
}
Thanks kevin The code you are using there is awesome Looking good so far, however it keeps returning permission denied... is it some setting in the android manifest?
Actually "Permission denied" often also means "no such file or directory" on android :-/ . It's very frustrating.
Try running something simple to start with like:
cmd.sh.runWaitFor("echo foo");
[email protected] said:
Actually "Permission denied" often also means "no such file or directory" on android :-/ . It's very frustrating.
Try running something simple to start with like:
cmd.sh.runWaitFor("echo foo");
Click to expand...
Click to collapse
yep, i tried running echo as i was confused by the "permission denied" although i had already set write permissions for the sdcard... didn't work, for some odd reason
Hi.
I'm currently working on a wakeup-app (alarm) that plays your favorite radio-station via shoutcast-stream.
It looked really easy:
Code:
MediaPlayer mp = new MediaPlayer();
mp.setDataSource(PATH_TO_FILE);
mp.prepare();
mp.start();
Then I found out Eclair doesn't support shoutcast's ICY protocol. Grmbl.
So for 2.1 (and earlier versions) it is required to handle the shoutcast-stream seperatly and pass it to MediaPlayer via http.
I even found an example:
http://code.google.com/p/npr-android-app/source/browse/trunk/Npr/src/org/npr/android/news/StreamProxy.java
Problem is that it's all a bit too advanced for me, so I was wondering if anyone has a really simple example of how to do this?
Something like:
"Add 'this file' to your project (where 'this file' would be a class similarly to StreamProxy) and then add this to your application:"
Code:
StreamProxy sp = new StreamProxy();
sp.setDataSource( SHOUTCAST_URL );
MediaPlayer mp = new MediaPlayer();
mp.setDataSource( sp.getProxyUrl );
mp.prepare();
mp.start();
(Pardon my english
fixed!
Will post how I did it soon.
Here's the app:
http://android.rejh.nl/fmalarm
Just got reminded by someone that I never posted the solution. Here goes:
Code:
// Streamurl
String streamUrl = "[Path-to-audio-stream-or-file]";
// Prepare Proxy (for Android 2.1 en lower (sdk<8))
sdkVersion = 0;
try { sdkVersion = Integer.parseInt(Build.VERSION.SDK); } // Note: Build.VERSION.SDK is deprecated..
catch (NumberFormatException e) {}
if (sdkVersion<8) {
if (proxy==null) {
try {
proxy = new StreamProxy();
proxy.init();
proxy.start();
streamUrl = String.format("http://127.0.0.1:%d/%s", proxy.getPort(), url);
} catch(IllegalStateException e) { Log.e(LOGTAG," -> IllStateException: "+e, e); }
}
}
// Create mediaplayer and set stream (proxied if needed)
mp = new MediaPlayer();
mp.setDataSource(streamUrl);
UPDATE!! Forgot the StreamProxy code
Code:
// Copyright 2010 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Minor changes by REJH Gadellaa 2010/2011
package org.npr.android.news;
import android.util.Log;
import org.apache.http.Header;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpResponseFactory;
import org.apache.http.ParseException;
import org.apache.http.ProtocolVersion;
import org.apache.http.RequestLine;
import org.apache.http.StatusLine;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.ClientConnectionOperator;
import org.apache.http.conn.OperatedClientConnection;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.DefaultClientConnection;
import org.apache.http.impl.conn.DefaultClientConnectionOperator;
import org.apache.http.impl.conn.DefaultResponseParser;
import org.apache.http.impl.conn.SingleClientConnManager;
import org.apache.http.io.HttpMessageParser;
import org.apache.http.io.SessionInputBuffer;
import org.apache.http.message.BasicHttpRequest;
import org.apache.http.message.BasicHttpResponse;
import org.apache.http.message.BasicLineParser;
import org.apache.http.message.ParserCursor;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.CharArrayBuffer;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.StringTokenizer;
public class StreamProxy implements Runnable {
private static final String LOG_TAG = "FMA2Proxy";
private int port = 5050;
public int getPort() {
return port;
}
private boolean isRunning = true;
private ServerSocket socket;
private Thread thread;
public void init() {
try {
socket = new ServerSocket(port, 0, InetAddress.getByAddress(new byte[] {127,0,0,1}));
socket.setSoTimeout(5000);
port = socket.getLocalPort();
Log.d(LOG_TAG, "port " + port + " obtained");
} catch (UnknownHostException e) {
Log.e(LOG_TAG, "Error initializing server", e);
} catch (IOException e) {
Log.e(LOG_TAG, "Error initializing server", e);
}
}
public void start() {
if (socket == null) {
throw new IllegalStateException("Cannot start proxy; it has not been initialized.");
}
thread = new Thread(this);
thread.start();
}
public void stop() {
isRunning = false;
if (thread == null) {
throw new IllegalStateException("Cannot stop proxy; it has not been started.");
}
if (socket!=null) { try {
socket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} }
thread.interrupt();
try {
thread.join(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// @Override
public void run() {
Log.d(LOG_TAG, "running");
while (isRunning) {
try {
Socket client = socket.accept();
if (client == null) {
continue;
}
Log.d(LOG_TAG, "client connected");
HttpRequest request = readRequest(client);
processRequest(request, client);
} catch (SocketTimeoutException e) {
// Do nothing
} catch (IOException e) {
Log.e(LOG_TAG, "Error connecting to client", e);
}
}
Log.d(LOG_TAG, "Proxy interrupted. Shutting down.");
}
private HttpRequest readRequest(Socket client) {
HttpRequest request = null;
InputStream is;
String firstLine;
try {
is = client.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
firstLine = reader.readLine();
} catch (IOException e) {
Log.e(LOG_TAG, "Error parsing request", e);
return request;
}
if (firstLine == null) {
Log.i(LOG_TAG, "Proxy client closed connection without a request.");
return request;
}
StringTokenizer st = new StringTokenizer(firstLine);
String method = st.nextToken();
String uri = st.nextToken();
Log.d(LOG_TAG, uri);
String realUri = uri.substring(1);
Log.d(LOG_TAG, realUri);
request = new BasicHttpRequest(method, realUri);
return request;
}
private HttpResponse download(String url) {
DefaultHttpClient seed = new DefaultHttpClient();
SchemeRegistry registry = new SchemeRegistry();
registry.register(
new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
SingleClientConnManager mgr = new MyClientConnManager(seed.getParams(),
registry);
DefaultHttpClient http = new DefaultHttpClient(mgr, seed.getParams());
HttpGet method = new HttpGet(url);
HttpResponse response = null;
try {
Log.d(LOG_TAG, "starting download");
response = http.execute(method);
Log.d(LOG_TAG, "downloaded");
} catch (ClientProtocolException e) {
Log.e(LOG_TAG, "Error downloading", e);
} catch (IOException e) {
Log.e(LOG_TAG, "Error downloading", e);
}
return response;
}
private void processRequest(HttpRequest request, Socket client)
throws IllegalStateException, IOException {
if (request == null) {
return;
}
Log.d(LOG_TAG, "processing");
String url = request.getRequestLine().getUri();
HttpResponse realResponse = download(url);
if (realResponse == null) {
return;
}
Log.d(LOG_TAG, "downloading...");
InputStream data = realResponse.getEntity().getContent();
StatusLine line = realResponse.getStatusLine();
HttpResponse response = new BasicHttpResponse(line);
response.setHeaders(realResponse.getAllHeaders());
Log.d(LOG_TAG, "reading headers");
StringBuilder httpString = new StringBuilder();
httpString.append(response.getStatusLine().toString());
httpString.append("\n");
for (Header h : response.getAllHeaders()) {
httpString.append(h.getName()).append(": ").append(h.getValue()).append(
"\n");
}
httpString.append("\n");
Log.d(LOG_TAG, "headers done");
try {
byte[] buffer = httpString.toString().getBytes();
int readBytes = -1;
Log.d(LOG_TAG, "writing to client");
client.getOutputStream().write(buffer, 0, buffer.length);
// Start streaming content.
byte[] buff = new byte[1024 * 50];
while (isRunning && (readBytes = data.read(buff, 0, buff.length)) != -1) {
client.getOutputStream().write(buff, 0, readBytes);
}
} catch (Exception e) {
Log.e("", e.getMessage(), e);
} finally {
if (data != null) {
data.close();
}
client.close();
}
}
private class IcyLineParser extends BasicLineParser {
private static final String ICY_PROTOCOL_NAME = "ICY";
private IcyLineParser() {
super();
}
@Override
public boolean hasProtocolVersion(CharArrayBuffer buffer,
ParserCursor cursor) {
boolean superFound = super.hasProtocolVersion(buffer, cursor);
if (superFound) {
return true;
}
int index = cursor.getPos();
final int protolength = ICY_PROTOCOL_NAME.length();
if (buffer.length() < protolength)
return false; // not long enough for "HTTP/1.1"
if (index < 0) {
// end of line, no tolerance for trailing whitespace
// this works only for single-digit major and minor version
index = buffer.length() - protolength;
} else if (index == 0) {
// beginning of line, tolerate leading whitespace
while ((index < buffer.length()) &&
HTTP.isWhitespace(buffer.charAt(index))) {
index++;
}
} // else within line, don't tolerate whitespace
if (index + protolength > buffer.length())
return false;
return buffer.substring(index, index + protolength).equals(ICY_PROTOCOL_NAME);
}
@Override
public Header parseHeader(CharArrayBuffer buffer) throws ParseException {
return super.parseHeader(buffer);
}
@Override
public ProtocolVersion parseProtocolVersion(CharArrayBuffer buffer,
ParserCursor cursor) throws ParseException {
if (buffer == null) {
throw new IllegalArgumentException("Char array buffer may not be null");
}
if (cursor == null) {
throw new IllegalArgumentException("Parser cursor may not be null");
}
final int protolength = ICY_PROTOCOL_NAME.length();
int indexFrom = cursor.getPos();
int indexTo = cursor.getUpperBound();
skipWhitespace(buffer, cursor);
int i = cursor.getPos();
// long enough for "HTTP/1.1"?
if (i + protolength + 4 > indexTo) {
throw new ParseException
("Not a valid protocol version: " +
buffer.substring(indexFrom, indexTo));
}
// check the protocol name and slash
if (!buffer.substring(i, i + protolength).equals(ICY_PROTOCOL_NAME)) {
return super.parseProtocolVersion(buffer, cursor);
}
cursor.updatePos(i + protolength);
return createProtocolVersion(1, 0);
}
@Override
public RequestLine parseRequestLine(CharArrayBuffer buffer,
ParserCursor cursor) throws ParseException {
return super.parseRequestLine(buffer, cursor);
}
@Override
public StatusLine parseStatusLine(CharArrayBuffer buffer,
ParserCursor cursor) throws ParseException {
StatusLine superLine = super.parseStatusLine(buffer, cursor);
return superLine;
}
}
class MyClientConnection extends DefaultClientConnection {
@Override
protected HttpMessageParser createResponseParser(
final SessionInputBuffer buffer,
final HttpResponseFactory responseFactory, final HttpParams params) {
return new DefaultResponseParser(buffer, new IcyLineParser(),
responseFactory, params);
}
}
class MyClientConnectionOperator extends DefaultClientConnectionOperator {
public MyClientConnectionOperator(final SchemeRegistry sr) {
super(sr);
}
@Override
public OperatedClientConnection createConnection() {
return new MyClientConnection();
}
}
class MyClientConnManager extends SingleClientConnManager {
private MyClientConnManager(HttpParams params, SchemeRegistry schreg) {
super(params, schreg);
}
@Override
protected ClientConnectionOperator createConnectionOperator(
final SchemeRegistry sr) {
return new MyClientConnectionOperator(sr);
}
}
}
I'm an experienced developer but new to Android development. I have an app that runs some native binaries, and I provide a status indicator to show when the native process is running and when it's not. Currently I poll the device to figure this out, using the ActivityManager API to determine if specific processes are running or not.
I'm hoping there is some way to register a listener on process state changes, so I can get notified when my process starts or stops. I looked through the API, and there doesn't seem to be such a thing. Does anyone know how I can keep track of process start and stop other than polling via ActivityManager?
MidnightJava said:
I'm an experienced developer but new to Android development. I have an app that runs some native binaries, and I provide a status indicator to show when the native process is running and when it's not. Currently I poll the device to figure this out, using the ActivityManager API to determine if specific processes are running or not.
I'm hoping there is some way to register a listener on process state changes, so I can get notified when my process starts or stops. I looked through the API, and there doesn't seem to be such a thing. Does anyone know how I can keep track of process start and stop other than polling via ActivityManager?
Click to expand...
Click to collapse
Afaik there's no way to accomplish that other than your way or being system/root app. See this similar question here for reference.
Can you show how you start the process?
EmptinessFiller said:
Can you show how you start the process?
Click to expand...
Click to collapse
Sure. Here's the class that manages starting, stopping, and statusing (running or not) the binary executable. In this case, it's the omniNames service of the omni ORB (CORBA broker).
Code:
public class RHManager {
private TimerTask task = new TimerTask() {
@Override
public void run() {
if (RHManager.this.listener != null) {
listener.running(isOmniNamesRunning());
}
}
};
private IStatusListener listener;
public RHManager() {
}
public void startOmniNames() {
final Exec exec = new Exec();
final String[] args = new String[]
{RhMgrConstants.INSTALL_LOCATION_OMNI_NAMES_SCRIPTS + "/" + RhMgrConstants.OMNI_NAMES_SCRIPT_FILE,
"start"};
final String[] env = new String[] {"LD_LIBRARY_PATH=/sdcard/data/com.axiosengineering.rhmanager/omniORB/lib"};
Thread t = new Thread() {
public void run() {
try {
int res = exec.doExec(args, env);
logMsg("omniNames start return code " + res);
} catch (IOException e) {
logMsg("Failed to start omniNames");
e.printStackTrace();
}
String std = exec.getOutResult();
logMsg("omniNames start: std out==> " + std );
String err = exec.getErrResult();
logMsg("omniNames start: err out==> " + err );
};
};
t.start();
logMsg("omniNames started");
}
private boolean isOmniNamesRunning() {
String pid_s = getOmniNamesPid();
Integer pid = null;
if (pid_s != null) {
try {
pid = Integer.parseInt(pid_s);
} catch (NumberFormatException e) {
return false;
}
}
if (pid != null) {
RunningAppProcessInfo activityMgr = new ActivityManager.RunningAppProcessInfo("omniNames", pid, null);
return activityMgr.processName != null ;
}
return false;
}
public void stopOmniNames() {
String pid = getOmniNamesPid();
android.os.Process.killProcess(Integer.parseInt(pid));
android.os.Process.sendSignal(Integer.parseInt(pid), android.os.Process.SIGNAL_KILL);
}
private String getOmniNamesPid() {
Exec exec = new Exec();
final String[] args = new String[]
{RhMgrConstants.INSTALL_LOCATION_OMNI_NAMES_SCRIPTS + "/" + RhMgrConstants.OMNI_NAMES_SCRIPT_FILE,
"pid"};
String pid = "";
try {
int res = exec.doExec(args, null);
logMsg("oniNames pid return code: " + res);
} catch (IOException e) {
logMsg("Failed to start omniNames");
e.printStackTrace();
return pid;
}
String std = exec.getOutResult();
logMsg("omniNames pid: std out ==> " + std);
String err = exec.getErrResult();
logMsg("omniNames pid: err out ==> " + err);
String[] parts = std.split("\\s+");
if (parts.length >= 2) {
pid = parts[1];
}
return pid;
}
//monitor omniNames status and report status periodically to an IStatusListener
public void startMonitorProcess(IStatusListener listener, String string) {
this.listener = listener;
Timer t = new Timer();
t.schedule(task, 0, 1000);
}
private void logMsg(String msg) {
if (RhMgrConstants.DEBUG) {
System.err.println(msg);
}
}
}
Here's the Exec class that handles invocation of Runtime#exec(), consumes std and err out, and reports those and process return status to the caller.
Code:
public class Exec {
private String outResult;
private String errResult;
private Process process;
private boolean failed = false;
StreamReader outReader;
StreamReader errReader;
public int doExec(String[] cmd, String[] envp) throws IOException{
Timer t = null;
try {
process = Runtime.getRuntime().exec(cmd, envp);
outReader = new StreamReader(process.getInputStream());
outReader.setPriority(10);
errReader = new StreamReader(process.getErrorStream());
outReader.start();
errReader.start();
t = new Timer();
t.schedule(task, 10000);
int status = process.waitFor();
outReader.join();
errReader.join();
StringWriter outWriter = outReader.getResult();
outResult = outWriter.toString();
outWriter.close();
StringWriter errWriter = errReader.getResult();
errResult = errWriter.toString();
errWriter.close();
return (failed ? -1: status);
} catch (InterruptedException e) {
return -1;
} finally {
if (t != null) {
t.cancel();
}
}
}
public int doExec(String[] cmd) throws IOException{
return doExec(cmd, null);
}
public String getOutResult(){
return outResult;
}
public String getErrResult(){
return errResult;
}
private static class StreamReader extends Thread {
private InputStream is;
private StringWriter sw;
StreamReader(InputStream is) {
this.is = is;
sw = new StringWriter(30000);
}
public void run() {
try {
int c;
while ((c = is.read()) != -1){
sw.write(c);
}
}
catch (IOException e) { ; }
}
StringWriter getResult() {
try {
is.close();
} catch (IOException e) {
System.err.println("Unable to close input stream in StreamReader");
}
return sw;
}
}
private TimerTask task = new TimerTask() {
@Override
public void run() {
failed = true;
process.destroy();
}
};
}
Here's the script that startOminNames() invokes. It's the shell script installed with omniORB with functions other than start and get_pid removed, since those are handled by Android classes. You can invoke any executable in place of the script, or wrap your executable in a script.
Code:
#
# omniNames init file for starting up the OMNI Naming service
#
# chkconfig: - 20 80
# description: Starts and stops the OMNI Naming service
#
exec="/sdcard/data/com.axiosengineering.rhmanager/omniORB/bin/omniNames"
prog="omniNames"
logdir="/sdcard/data/com.axiosengineering.rhmanager/omniORB/logs"
logfile="/sdcard/data/com.axiosengineering.rhmanager/omniORB/logs/omninames-localhost.err.log"
options=" -start -always -logdir $logdir -errlog $logfile"
start() {
#[ -x $exec ] || exit 5
echo -n $"Starting $prog: "
$exec $options
}
get_pid() {
ps | grep omniNames
}
case "$1" in
start)
start && exit 0
$1
;;
pid)
get_pid
;;
*)
echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}"
exit 2
esac
exit $?
And here's the IStatusListener interface
Code:
public interface IStatusListener {
public void running(boolean running);
}
Runtime.exec() has some pitfalls. See this helpful Runtime.exec tutorial for a nice explanation.
And you may also want to check out this post on loading native binaries in Android.
//sorry for my english, if any
Hi. I'm using AIDE right on my device (Xperia NeoV, 4.1.B.0.587, root)
And i'm trying to inject key events through the same shell command:
"input keyevent 25" via "su", both in AIDE Console and separated Activity.
//volume-down key is not my target, just good for tests. I've tried different keys, same result.
Part of the code:
Code:
try {
java.lang.Process p = Runtime.getRuntime().exec("su");
DataOutputStream dos = new DataOutputStream(p.getOutputStream());
dos.writeBytes("input keyevent 25\n");
dos.flush();
} catch (IOException ex) {
//log any errors
}
So, when it goes in AIDE ConsoleApp - it pushes down volume.
But when it executes from my Activity - nothing happens (no error messages in log, dos != null);
Maybe there should be some specific permission on manifest?
"android.permission.ACCESS_SUPERUSER" - no changes.
Maybe there should be some trick in code? Or(and?) i'm very stupid. Also, maybe somewhere a whole listing of _simple_ keyInjection project exists?
I managed to solve it already. I'm using this class now:
public void RunAsRoot(String[] cmds){
Process p = null;
try {
p = Runtime.getRuntime().exec("su");
} catch (IOException e) {
e.printStackTrace();
}
DataOutputStream os = new DataOutputStream(p.getOutputStream());
for (String tmpCmd : cmds) {
try {
os.writeBytes(tmpCmd+"\n");
} catch (IOException e) {
e.printStackTrace();
}
}
try {
os.writeBytes("exit\n");
} catch (IOException e) {
e.printStackTrace();
}
try {
os.flush();
} catch (IOException e) {
e.printStackTrace();
}
Click to expand...
Click to collapse
Then simply call it via
String[] commands = {"command1", "command2", "command3", ...};
RunAsRoot(commands);
Click to expand...
Click to collapse
Thanks anyways!
Click to expand...
Click to collapse
Credits: @KrauseDroid
Our original thread i took it from:
http://forum.xda-developers.com/showthread.php?t=2725173
---------------------------------
Phone : Nexus 4
OS:
Pure KitKat 4.4.2 stock, no root, no mods
---------------------------------
4d 61 73 72 65 70 75 73 20 66 74 77
Gesendet von Tapatalk
Masrepus said:
Credits: @KrauseDroid
Our original thread i took it from:
http://forum.xda-developers.com/showthread.php?t=2725173
Click to expand...
Click to collapse
I saw that thread before creating own, and tried solutions from it. They works same way - only from ConsoleApp in AIDE.
Also, last method is the same as I'm already using, but I've tried to copy code in project - no progress.
In addition:
- superuser rights granted to both.
- test "ls" command put into dos object gives me same list of current dir in both projects, so commands seems to run.
Listing:
MainActivity:
Code:
package com.tsk.mk;
import android.app.*;
import android.content.*;
import android.os.*;
import android.widget.*;
import java.io.*;
public class MainActivity extends Activity {
public static TextView logView;
[user=439709]@override[/user]
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
logView = (TextView) findViewById(R.id.log);
log("creating sercice");
final MainActivity me = this;
new Thread() {
[user=439709]@override[/user]
public void run() {
log("thread.run");
startService(new Intent(me, MissedKeysService.class));
log("thread: service shoul'd be created");
}
}.start();
log("end of MA.onCreate method");
}
static void log(String message) {
logView.setText(logView.getText() + "\n" + message);
}
}
MissedKeysService:
Code:
package com.tsk.mk;
import android.app.*;
import android.content.*;
import android.graphics.*;
import android.os.*;
import android.view.*;
import java.io.*;
public class MissedKeysService extends Service {
private WindowManager windowManager;
private MissedKeysView mkv;
private WindowManager.LayoutParams params;
private DataOutputStream dos;
[user=439709]@override[/user]
public IBinder onBind(Intent intent) {
MainActivity.log("onBind called o_o");
return null;
}
[user=439709]@override[/user]
public void onCreate() {
MainActivity.log("Service.onCreate");
super.onCreate();
try {
java.lang.Process p = Runtime.getRuntime().exec("su");
dos = new DataOutputStream(p.getOutputStream());
} catch (IOException ex) {
MainActivity.log(ex.getMessage());
dos = null;
}
windowManager = (WindowManager)
getSystemService(WINDOW_SERVICE);
mkv = new MissedKeysView(this, this);
params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.RIGHT | Gravity.CENTER_VERTICAL;
params.width = MissedKeysView.keySize;
params.height = MissedKeysView.keySize;
windowManager.addView(mkv, params);
MainActivity.log("Service started");
}
[user=439709]@override[/user]
public void onDestroy () {
super.onDestroy ();
if (mkv != null) windowManager.removeView(mkv);
MainActivity.log("Ssrvice is ended");
}
public void extend(boolean state) {
params.height = mkv.keySize * (state ? MissedKeysView.keys : 1);
windowManager.updateViewLayout(mkv, params);
}
public void sendKey(int code) {
if (dos == null) MainActivity.log("dos is null");
try {
dos.writeBytes("input keyevent " + code + "\n");
dos.flush();
MainActivity.log("" + code);
} catch (IOException e) {
MainActivity.log("wtf?");
}
}
}
MissedKeysView:
Code:
package com.tsk.mk;
import android.content.*;
import android.graphics.*;
import android.view.*;
import android.view.View.*;
import java.io.*;
public class MissedKeysView extends View
implements OnTouchListener {
final static int keySize = 64;
final static String[] labels = {":", "+", "-", "^", "v", "o", "<", ">"};
final private int[] codes = {-1, 24, 25, 19, 20, 23, 21, 22};
final static int keys = 3; //max shown keys
MissedKeysService mks;
Paint bgP; //background
Paint hbgP; //highlighted bg
Paint tP; //text
int selected = -1; //active key
public MissedKeysView(Context context, MissedKeysService mks) {
super(context);
this.mks = mks;
bgP = new Paint();
bgP.setARGB(64, 128, 128, 128);
hbgP = new Paint();
hbgP.setARGB(64, 255, 128, 0);
tP = new Paint();
tP.setARGB(128, 255, 255, 255);
tP.setTextSize(keySize);
tP.setTextAlign(Paint.Align.CENTER);
setOnTouchListener(this);
}
[user=439709]@override[/user]
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for(int i=0; i<getHeight()/keySize; i++) {
canvas.drawCircle(keySize/2,
keySize/2 + i*keySize, keySize/2,
(i == selected ? hbgP : bgP));
canvas.drawText(labels[i], keySize/2, i*keySize + keySize*3/4, tP);
}
}
[user=439709]@override[/user]
public boolean onTouch(View v, MotionEvent event) {
switch(event.getAction()) {
case MotionEvent.ACTION_DOWN:
selected = (int) (event.getY()/keySize);
if (selected == 0) mks.extend((int) getHeight() <= keySize);
else mks.sendKey(codes[selected]);
break;
case MotionEvent.ACTION_UP:
selected =-1;
}
invalidate();
return super.onTouchEvent(event);
}
}
AndroidManifest:
Code:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.tsk.mk"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="11" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<activity
android:label="@string/app_name"
android:name=".MainActivity" >
<intent-filter >
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".MissedKeysService"
android:exported="true" >
</service>
</application>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.ACCESS_SUPERUSER" />
<uses-permission android:name="android.permission.INJECT_EVENTS" />
</manifest>
@Torsionick do you have the permission INJECT_EVENTS
---------------------------------
Phone : Nexus 4
OS:
Pure KitKat 4.4.2 stock, no root, no mods
---------------------------------
4d 61 73 72 65 70 75 73 20 66 74 77
Gesendet von Tapatalk
Masrepus said:
@Torsionick do you have the permission INJECT_EVENTS
Click to expand...
Click to collapse
Thanks for participating in solving.
The reason was somewhere else - after executing "export LD_LIBRARY_PATH=/system/lib" all works fine. Thread is over.
I am new to Java and I am getting kind of stuck by trying to loop through my JSON. I am retrieving a JSON object where I want to loop through.
My JSON looks as follow:
Code:
{"message":{"2":[{"uid":"2","title":"","message":"Test1","success":1,"created_at":null,"updated_at":null}],"3":[{"uid":"3","title":"","message":"Test2 !","success":1,"created_at":null,"updated_at":null}],"4":[{"uid":"4","title":"Bla","message":"Test3!","success":1,"created_at":null,"updated_at":null}]}}
I tried a loop like this:
Code:
for(int i = 0; i<json.names().length(); i++){
try {
Log.v("TEST", "key = " + json.names().getString(i) + " value = " + json.get(json.names().getString(i)));
} catch (JSONException e) {
e.printStackTrace();
}
}
But this will only target "message" which includes the whole JSON "string". I want to loop through each message and retrieving the value of uid 1, uid 2 etc. How can I achieve this?
Thanks in advance.
Here is how I'm doing it:
Code:
private static final String AllNewsItemsURL = "some_url_here.php";
private static final String TAG_SUCCESS = "success";
private static final String NEWS = "news";
private static final String TITLE = "title";
private static final String STORY = "story";
private final JSONParser jParser = new JSONParser();
private JSONArray newsItems = null;
..... / code snipped / ....
try {
JSONObject json = jParser.makeHttpRequest(AllNewsItemsURL, params);
int success = json.getInt(TAG_SUCCESS);
if (success == 1) {
newsItems = json.getJSONArray(NEWS);
for (int i = 0; i < newsItems.length(); i++) {
JSONObject obj = newsItems.getJSONObject(i);
Integer id = i + 1;
String title = obj.getString(TITLE);
String story = obj.getString(STORY);
}
} else {
Log.e("JSON Response", "success == 0");
}
} catch (Exception e) {
e.printStackTrace();
}
Thanks for your reply. I tried it but getting this exception:
Code:
org.json.JSONException: Value {"2":[{"uid":"2","title":"","message":"Test1","success":1,"created_at":null,"updated_at":null}],"3":[{"uid":"3","title":"","message":"Test2","success":1,"created_at":null,"updated_at":null}],"4":[{"uid":"4","title":"Bla","message":"Test3","success":1,"created_at":null,"updated_at":null}]} at messages of type org.json.JSONObject cannot be converted to JSONArray
CodeMonkeyy said:
Thanks for your reply. I tried it but getting this exception:
Code:
org.json.JSONException: Value {"2":[{"uid":"2","title":"","message":"Test1","success":1,"created_at":null,"updated_at":null}],"3":[{"uid":"3","title":"","message":"Test2","success":1,"created_at":null,"updated_at":null}],"4":[{"uid":"4","title":"Bla","message":"Test3","success":1,"created_at":null,"updated_at":null}]} at messages of type org.json.JSONObject cannot be converted to JSONArray
Click to expand...
Click to collapse
You might want to try the matching JSONParser I have for it, sorry forgot to include it:
https://github.com/JonnyXDA/WGSB/bl...om/jonny/wgsb/material/parser/JSONParser.java
Also noting that your entire JSON Array is called "message" but you also have a parameter called "message" - maybe rename the Array to "messages"?
As for the code you should have something like:
Code:
private static final String AllNewsItemsURL = "some_url_here.php";
private static final String TAG_SUCCESS = "success";
private static final String MESSAGES = "messages";
private static final String TITLE = "title";
private static final String MESSAGE = "message";
private final JSONParser jParser = new JSONParser();
private JSONArray messageItems = null;
..... / code snipped / ....
try {
JSONObject json = jParser.makeHttpRequest(AllNewsItemsURL, params);
int success = json.getInt(TAG_SUCCESS);
if (success == 1) {
messageItems = json.getJSONArray(MESSAGES);
for (int i = 0; i < messageItems.length(); i++) {
JSONObject obj = messageItems.getJSONObject(i);
String title = obj.getString(TITLE);
String message = obj.getString(MESSAGE);
}
} else {
Log.e("JSON Response", "success == 0");
}
} catch (Exception e) {
e.printStackTrace();
}
My code looks like the this:
Code:
//Message task
MessageTask task = new MessageTask(DashboardActivity.class);
task.execute();
try {
json = task.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
try {
int success = json.getInt(TAG_SUCCESS);
if (success == 1) {
messageItems = json.getJSONArray(MESSAGES);
for (int i = 0; i < messageItems.length(); i++) {
JSONObject obj = messageItems.getJSONObject(i);
String title = obj.getString(TITLE);
String message = obj.getString(MESSAGE);
}
} else {
Log.e("JSON Response", "success == 0");
}
} catch (Exception e) {
e.printStackTrace();
}
I am using Async Task to retrieve my JSON.
And my JSON parser looks like this:
Code:
public class JSONParser {
static InputStream is = null;
static JSONObject jObj = null;
static String json = "";
// constructor
public JSONParser() {
}
public JSONObject getJSONFromUrl(String url, List<NameValuePair> params) {
// Making HTTP request
try {
// defaultHttpClient
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(url);
httpPost.setEntity(new UrlEncodedFormEntity(params));
HttpResponse httpResponse = httpClient.execute(httpPost);
HttpEntity httpEntity = httpResponse.getEntity();
is = httpEntity.getContent();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(
is, "iso-8859-1"), 8);
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
is.close();
json = sb.toString();
Log.e("JSON", json);
} catch (Exception e) {
Log.e("Buffer Error", "Error converting result " + e.toString());
}
// try parse the string to a JSON object
try {
jObj = new JSONObject(json);
} catch (JSONException e) {
Log.e("JSON Parser", "Error parsing data " + e.toString());
}
// return JSON String
return jObj;
}
}
But it doesn't get through the if statement, because it can't find the value success. ( org.json.JSONException: No value for success ). Don't really know what I am doing wrong here. Is it because I am using AsyncTask and retrieving my JSON the wrong way?
I also renamed my Array to "messages", stupid mistake thanks!
CodeMonkeyy said:
But it doesn't get through the if statement, because it can't find the value success. ( org.json.JSONException: No value for success ). Don't really know what I am doing wrong here. Is it because I am using AsyncTask and retrieving my JSON the wrong way?
I also renamed my Array to "messages", stupid mistake thanks!
Click to expand...
Click to collapse
We're getting closer! With regards to using AsyncTask - thats fine and the recommended way to do service side sync/download operations (doesn't block the UI thread) so no need to change that.
I just took a look at my reference JSON and I have the success tag outside of an item eg:
Code:
{"topical":[{"tid":"5","title":"Exam countdown... just 12 weeks left!","story":"some_story_text_here","staff":"0","red":"0","show":"1"}],[COLOR="red"]"success":1[/COLOR]}
Whereas your success tag is put in each item:
Code:
{"message":{"2":[{"uid":"2","title":"","message":"Test1","[COLOR="red"]success":1,[/COLOR]"created_at":null,"updated_at":null}],"3":[{"uid":"3","title":"","message":"Test2 !",[COLOR="red"]"success":1[/COLOR],"created_at":null,"updated_at":null}],"4":[{"uid":"4","title":"Bla","message":"Test3!","[COLOR="Red"]success":1[/COLOR],"created_at":null,"updated_at":null}]}}
I'm guessing that your php line for:
PHP:
$response["success"] = 1;
is inside of the while loop:
PHP:
while ($row = mysql_fetch_array($result)) {
Taking it out of the while loop should fix that
That makes sense, because I am trying to get a success code for my whole JSON response. I changed my PHP code to:
Code:
while($row = mysqli_fetch_array( $messages )) {
// create rowArr
$rowArr = array(
'uid' => $row['id'],
'title' => $row['title'],
'message' => $row["message"],
'created_at' => $row['created_at'],
'updated_at' => $row['updated_at'],
);
// store rowArr in $return_arr
$return_arr[$row['id']][] = $rowArr;
}
$return_arr['success'] = 1;
// Json encode
echo json_encode(array("messages" => $return_arr));
}
Retrieving the following JSON:
Code:
{"messages":{"2":[{"uid":"2","title":"","message":"Test1","created_at":null,"updated_at":null}],"3":[{"uid":"3","title":"","message":"Test2 !","created_at":null,"updated_at":null}],"4":[{"uid":"4","title":"Bla","message":"Test3!","created_at":null,"updated_at":null}],"success":1}}
But I am still getting the following exception:
Code:
org.json.JSONException: No value for success
The success tag is still being encoded as part of an inner array, not the first array - try this:
PHP:
if (mysql_num_rows($messages) > 0) {
$response["messages"] = array();
while ($row = mysql_fetch_array($messages)) {
$messagesArray= array(
'uid' => $row['id'],
'title' => $row['title'],
'message' => $row['message'],
'created_at' => $row['created_at'],
'updated_at' => $row['updated_at'],
);
array_push($response["messages"], $messagesArray);
}
$response["success"] = 1;
echo json_encode($response);
} else {
$response["success"] = 0;
$response["message"] = "No messages found";
echo json_encode($response);
}
And it's finally working!
Getting the following JSON result:
Code:
{"tag":"message","success":1,"error":0,"messages":[{"uid":"2","title":"","message":"Test1","created_at":null,"updated_at":null},{"uid":"3","title":"","message":"Test2!","created_at":null,"updated_at":null},{"uid":"4","title":"Bla","message":"Test3!","created_at":null,"updated_at":null}]}
And I can successfully loop through my JSON with the following code:
Code:
try {
int success = json.getInt(TAG_SUCCESS);
if (success == 1) {
messageItems = json.getJSONArray(MESSAGES);
for (int i = 0; i < messageItems.length(); i++) {
JSONObject obj = messageItems.getJSONObject(i);
String title = obj.getString(TITLE);
String message = obj.getString(MESSAGE);
Log.e("TITLE :", title);
Log.e("MESSAGE :", message);
}
} else {
Log.e("JSON Response", "success == 0");
}
} catch (Exception e) {
e.printStackTrace();
}
Thank you very much! So the problem was that I placed the "success" tag outside my Array?